<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Stephan Miller</title>
    <description>Kansas City Software Engineer and Writer</description>
    <link>https://www.stephanmiller.com/</link>
    <atom:link href="https://www.stephanmiller.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 08 Feb 2026 17:58:01 -0600</pubDate>
    <lastBuildDate>Sun, 08 Feb 2026 17:58:01 -0600</lastBuildDate>
    <generator>Jekyll v4.2.2</generator>
    
      <item>
        <title>I Burned Out on Vibe Coding, Came Back, and Rewrote Everything</title>
        <description>&lt;p&gt;I hit a wall with vibe coding. Not a dramatic crash. More like the slow realization that I’d been sprinting for months and couldn’t remember why. I had 15 projects in various states of “maybe done,” a GitHub commit chart that looked like a heart monitor, and a growing suspicion that I was building things just to build things.&lt;/p&gt;

&lt;p&gt;Fortunately, freelance writing work picked up right around the same time. Enough to actually pay attention to it. So I stepped away from the side projects, wrote about other people’s technology for a change, and let my own code sit untouched for a few months.&lt;/p&gt;

&lt;p&gt;When I came back, I had no patience for bullshit. And I looked at my projects differently.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/93myIeRtsN0&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#your-vibe-coded-apps-are-prototypes-and-thats-fine&quot; id=&quot;markdown-toc-your-vibe-coded-apps-are-prototypes-and-thats-fine&quot;&gt;Your Vibe-Coded Apps Are Prototypes (And That’s Fine)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#making-adding-features-the-feature&quot; id=&quot;markdown-toc-making-adding-features-the-feature&quot;&gt;Making “Adding Features” the Feature&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#building-bottom-up-with-verdent-and-claude-code&quot; id=&quot;markdown-toc-building-bottom-up-with-verdent-and-claude-code&quot;&gt;Building Bottom-Up with Verdent and Claude Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-60-missing-apis&quot; id=&quot;markdown-toc-the-60-missing-apis&quot;&gt;The 60 Missing APIs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#making-plans-that-any-ai-agent-can-execute&quot; id=&quot;markdown-toc-making-plans-that-any-ai-agent-can-execute&quot;&gt;Making Plans That Any AI Agent Can Execute&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-same-pattern-different-project&quot; id=&quot;markdown-toc-the-same-pattern-different-project&quot;&gt;The Same Pattern, Different Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-changed&quot; id=&quot;markdown-toc-what-changed&quot;&gt;What Changed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;your-vibe-coded-apps-are-prototypes-and-thats-fine&quot;&gt;Your Vibe-Coded Apps Are Prototypes (And That’s Fine)&lt;/h2&gt;

&lt;p&gt;Here’s the thing I couldn’t see while I was in the thick of it: almost everything I’d built with AI coding tools was a prototype. Not in the dismissive sense. These apps worked. &lt;a href=&quot;https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/&quot;&gt;EmberText&lt;/a&gt; was a functional Electron writing app. Niche Site Factory could generate and manage content sites. They ran. They did things.&lt;/p&gt;

&lt;p&gt;But they were all built top-down. I’d tell the AI “build me an app that does X” and it would scaffold the whole thing, features and all, in one giant session. The problem is that when you build top-down with AI, you end up with something that works but is almost impossible to extend. Every new feature is a negotiation with the existing architecture. You’re not adding to the app. You’re fighting it.&lt;/p&gt;

&lt;p&gt;EmberText was the clearest example. I built it with Claude Code over about 16 hours and $80 in API costs. It had AI integration, text generation, character relationship graphs, plot scaffolding. Impressive on paper. But by the time I realized it should have had a plugin architecture, I was already deep enough that refactoring meant essentially starting over.&lt;/p&gt;

&lt;p&gt;So that’s what I did.&lt;/p&gt;

&lt;h2 id=&quot;making-adding-features-the-feature&quot;&gt;Making “Adding Features” the Feature&lt;/h2&gt;

&lt;p&gt;The insight that changed everything was stupid simple: instead of building an app with features, build an app where adding features &lt;em&gt;is&lt;/em&gt; the feature.&lt;/p&gt;

&lt;p&gt;I’d been using Obsidian for years and it’s in my top 5 favorite software. It’s incredible for notes, planning, and organization. You can even make it distraction-free for writing. It’s just not the default, and “not the default” matters more than you’d think when you’re trying to get into a flow state. I tried to hack around this with my Daily Prompts plugin that launched an alert and opened a daily note in Zen mode. It worked, kind of, but I was still fighting the tool.&lt;/p&gt;

&lt;p&gt;VS Code is for code. Obsidian is for notes. What’s for writing?&lt;/p&gt;

&lt;p&gt;That question led to Veneer, a complete rewrite of EmberText from scratch. Same idea, a distraction-free writing environment, but built from the ground up as a plugin-first architecture. The “Zen-First Shell” concept: when you open it, you see nothing but a clean sheet and your text. Sidebars, ribbons, status bars exist as ghost elements, hidden by default, appearing only when you hover near the edges or hit a hotkey. Everything that isn’t the writing surface has to earn its right to be on screen.&lt;/p&gt;

&lt;p&gt;And critically, every feature is a plugin. The file explorer? Plugin. The markdown editor? Plugin. The command palette? Plugin. Even core functionality ships as plugins that can be swapped, extended, or replaced. This isn’t just for a future community. It makes the whole thing dramatically easier to build with AI, because each plugin is a self-contained unit with clear boundaries. You can hand an AI agent a plugin spec and let it work without worrying about it breaking everything else.&lt;/p&gt;

&lt;h2 id=&quot;building-bottom-up-with-verdent-and-claude-code&quot;&gt;Building Bottom-Up with Verdent and Claude Code&lt;/h2&gt;

&lt;p&gt;I used &lt;a href=&quot;https://www.verdent.ai/&quot;&gt;Verdent&lt;/a&gt; to build the base application. If you read &lt;a href=&quot;https://www.stephanmiller.com/verdent-ai-when-your-ai-coding-assistant-finishes-before-you-can-get-coffee/&quot;&gt;my post about Verdent&lt;/a&gt;, you know this thing is fast. Too fast, honestly. It finished most of the base app, including a file browser sidebar plugin, a markdown editor plugin, and a command palette, in about 220 tokens, roughly $20 worth of credits. There were bugs left when I ran out of tokens, but the foundation was solid.&lt;/p&gt;

&lt;p&gt;But here’s where the process got interesting. Instead of just continuing to add features on top, I switched to Claude Code and did something I hadn’t done before: I asked it to &lt;em&gt;audit&lt;/em&gt; the codebase.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;use your skills and check the repo for best practices
- UI
- Is it themable like Obsidian or VS Code
- Plugin Architecture (and compare to VS Code and Obsidian)
- TypeScript
- Electron
- Structure, Naming Conventions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’d forgotten how many skills and plugins I had installed in Claude Code. When I ran this, it deployed four specialized agents in parallel:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Explore subagent&lt;/strong&gt; analyzed the overall project structure, UI patterns, theming, and naming conventions&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Architecture Strategist&lt;/strong&gt; evaluated system design decisions and compared the plugin architecture against VS Code and Obsidian&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Kieran TypeScript Reviewer&lt;/strong&gt; checked strict mode compliance, type safety, interface definitions, and generic patterns&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Best Practices Researcher&lt;/strong&gt; gathered industry standards and found examples from successful projects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not how I was working six months ago. Six months ago, I would have just told the AI to add the next feature and hoped for the best.&lt;/p&gt;

&lt;h2 id=&quot;the-60-missing-apis&quot;&gt;The 60 Missing APIs&lt;/h2&gt;

&lt;p&gt;The audit turned up a lot. Claude gave the codebase an A- (92/100) overall, which sounds great until you read the details. The critical finding was the plugin API gaps. Obsidian provides 60+ plugin APIs. Veneer was missing most of them.&lt;/p&gt;

&lt;p&gt;No modals. No notification system. No context menus anywhere. No way for plugins to subscribe to file or workspace events. No way to extend the CodeMirror editor. The native OS menu had “Open Folder” under “Veneer” instead of “File,” which is the kind of thing that makes you realize the AI built the structure but didn’t think about the conventions.&lt;/p&gt;

&lt;p&gt;I had Claude store all the findings in the project’s docs folder:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;BEST_PRACTICES_REVIEW.md&lt;/strong&gt;: Everything organized by priority with an implementation roadmap&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PLUGIN_API_GAPS.md&lt;/strong&gt;: A detailed comparison against Obsidian and VS Code showing exactly what was missing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;making-plans-that-any-ai-agent-can-execute&quot;&gt;Making Plans That Any AI Agent Can Execute&lt;/h2&gt;

&lt;p&gt;This is the part that parallels &lt;a href=&quot;https://mitchellh.com/writing/my-ai-adoption-journey&quot;&gt;Mitchell Hashimoto&apos;s AI adoption journey&lt;/a&gt;. He talks about “harness engineering,” the idea that every time an agent makes a mistake, you engineer a solution so it never makes that mistake again. Better implicit prompting. Actual programmed tools. The goal is building up an ecosystem where agents get better over time.&lt;/p&gt;

&lt;p&gt;I’m doing something similar, but at the project planning level. Instead of just fixing bugs as they come, I’m creating structured documentation that any AI tool can pick up and execute. My next prompt to Claude was:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Take docs/BEST_PRACTICES_REVIEW.md and docs/PLUGIN_API_GAPS.md and create a
markdown list of TODOs in the docs folder. These should be grouped into tasks
and subtasks. If it is possible to work on some tasks concurrently this should
be mentioned. This file should be able to be used by an AI agent to finish
these tasks. Add enough details to each task to speed up development time.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I have the work planned in 3 phases across 3 TODO files, plus a final phase listing every Obsidian plugin API that Veneer doesn’t have yet for future development. These are all in the docs folder of the project, version controlled, and written so that any agent, Claude Code, Jules, a VS Code extension with Qwen, whatever, can pick them up and start working.&lt;/p&gt;

&lt;p&gt;This is the difference between vibe coding and what I’m doing now. I’m still using AI to do the heavy lifting. But I’m not just throwing prompts at the wall. I’m using one AI tool to build, another to audit, and then creating structured plans that decouple the &lt;em&gt;what needs to happen&lt;/em&gt; from the &lt;em&gt;which tool does it&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-same-pattern-different-project&quot;&gt;The Same Pattern, Different Project&lt;/h2&gt;

&lt;p&gt;This isn’t just how I rebuilt Veneer. I’m doing the same thing with Niche Site Factory. Instead of telling an AI to “build me a niche site generator” (which is roughly what I did the first time), I started over by building the data model first.&lt;/p&gt;

&lt;p&gt;I took a real project, a sci-fi encyclopedia wiki, and used it to design the content structures. A knowledge graph in PostgreSQL with pgvector for embeddings. 2,622 books ingested into the entities table. Flexible JSONB storage that can handle books, concepts, authors, movies, whatever. The data model came first, the application came second.&lt;/p&gt;

&lt;p&gt;It’s the same bottom-up principle. Don’t build the house and then figure out the foundation. Build the foundation, verify it’s solid, then build up from there.&lt;/p&gt;

&lt;h2 id=&quot;what-changed&quot;&gt;What Changed&lt;/h2&gt;

&lt;p&gt;I think the burnout was actually useful. Stepping away let me see the pattern I was stuck in: build fast, hit a wall, start something new. That’s fine when you’re learning the tools. It’s how I figured out what Claude Code, Kiro, Verdent, and Jules are each good at. But at some point, you have to stop prototyping and start building.&lt;/p&gt;

&lt;p&gt;Here’s what’s different now:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Bottom-up, not top-down.&lt;/strong&gt; Start with the architecture and data model, not the features. Let the AI build on a solid foundation instead of improvising one.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Audit before extending.&lt;/strong&gt; Use AI review tools to find the gaps before you pile on more code. It’s cheaper to fix the structure now than refactor later.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Plans as portable artifacts.&lt;/strong&gt; Write TODO files detailed enough that any AI agent can execute them. Don’t marry yourself to one tool.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Plugins as a development strategy.&lt;/strong&gt; A plugin architecture isn’t just for the community. It makes AI-assisted development dramatically easier because each unit is self-contained.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Past work is research.&lt;/strong&gt; EmberText wasn’t a failure. It was a $80 prototype that taught me exactly what Veneer needed to be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m still &lt;a href=&quot;https://www.stephanmiller.com/category/vibe-coding/&quot;&gt;vibe coding&lt;/a&gt;. I’m just vibing with more structure now and calling it &lt;a href=&quot;https://www.stephanmiller.com/category/ai-assisted-development/&quot;&gt;AI-assisted development&lt;/a&gt;. And honestly, after a few months of writing for clients and not touching my own projects, coming back to this with fresh eyes and no patience might be the best thing that happened to any of them.&lt;/p&gt;
</description>
        <pubDate>Sun, 08 Feb 2026 01:00:00 -0600</pubDate>
        <link>https://www.stephanmiller.com/i-burned-out-on-vibe-coding-came-back-and-rewrote-everything/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/i-burned-out-on-vibe-coding-came-back-and-rewrote-everything/</guid>
        
        
        <category>ai-assisted-development</category>
        
        <category>vibe-coding</category>
        
      </item>
    
      <item>
        <title>Verdent AI - When Your AI Coding Assistant Finishes Before You Can Get Coffee</title>
        <description>&lt;p&gt;My current AI development process, if you want to call it that, is getting one AI tool working on one project while having another work on a different project. This only works if one or both projects aren’t near the end where I have to test and have AI fix a lot of things. This process grew out of the fact that sometimes it takes AI a little while to get a task done, but not enough time for you to get any real work done yourself. So it was either work on another project or scroll through Reddit.&lt;/p&gt;

&lt;p&gt;But Verdent put a kink in that plan.&lt;/p&gt;

&lt;p&gt;I needed something for Verdent to work on. When I had &lt;a href=&quot;https://www.stephanmiller.com/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work/&quot;&gt;Kiro build Obsidian plugins&lt;/a&gt;, one which was relatively simple, it created over a dozen tasks and took anywhere from two to four hours for each plugin. So I figured a couple of plugins would be enough work for a Saturday afternoon.&lt;/p&gt;

&lt;h2 id=&quot;the-projects-what-i-threw-at-verdent&quot;&gt;The Projects: What I Threw at Verdent&lt;/h2&gt;

&lt;p&gt;I had two ideas picked out to build and I used Auto Run mode to build both:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Obsidian Cleaner Plugin&lt;/strong&gt;: A “cleaner” plugin that checks that attachments in the Obsidian attachment folder and provide you with a checkbox list of all those that aren’t linked so you can delete them. It does the same thing with conflicted files. If I find any more common things I clean like this, I will add them later.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-cleaner.png&quot; alt=&quot;Obsidian Cleaner Plugin&quot; srcset=&quot;            /assets/resized/480/obsidian-cleaner.png 480w,            /assets/resized/800/obsidian-cleaner.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3D Tag Explorer Plugin&lt;/strong&gt;: A plugin that takes hierarchical tags like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;llm/writing/software&lt;/code&gt; and turns them into a 3D node graph with notes containing those tags included as the final nodes.&lt;/p&gt;

&lt;p&gt;Pretty straightforward stuff. I figured these would keep Verdent busy while I worked on something else.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-tag-explorer-3d.png&quot; alt=&quot;Obsidian 3D Tag Explorer Plugin&quot; srcset=&quot;            /assets/resized/480/obsidian-tag-explorer-3d.png 480w,            /assets/resized/800/obsidian-tag-explorer-3d.png 800w,            /assets/resized/1400/obsidian-tag-explorer-3d.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-reality-check-when-hours-becomes-minutes&quot;&gt;The Reality Check: When Hours Becomes Minutes&lt;/h2&gt;

&lt;p&gt;Verdent finished both of these Obsidian plugins in less than 15 minutes each.&lt;/p&gt;

&lt;p&gt;Now these plugins were relatively simple, but I did not expect that.&lt;/p&gt;

&lt;p&gt;There was one bug in the tag explorer where the background was above the node graph. I mentioned it to Verdent and it fixed it quickly. The cleaner plugin just worked on first try.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-chat-window.jpg&quot; alt=&quot;Verdent Chat Window&quot; srcset=&quot;            /assets/resized/480/verdent-chat-window.jpg 480w,            /assets/resized/800/verdent-chat-window.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So now I’m sitting here at 10:30 AM on a Saturday with two working plugins and I really wanted to focus on another task while Verdent just did its thing. This is not the kind of problem I expected to have with AI coding tools.&lt;/p&gt;

&lt;h2 id=&quot;panic-building-more-projects-to-feed-the-beast&quot;&gt;Panic Building: More Projects to Feed the Beast&lt;/h2&gt;

&lt;p&gt;I had to scramble to find more work for Verdent to do. Here’s what I threw at it next:&lt;/p&gt;

&lt;h3 id=&quot;bookforge&quot;&gt;BookForge&lt;/h3&gt;

&lt;p&gt;A web app, API, command line app, and library that converts markdown files into simple epubs. This was simple but I bet it took less than 30 minutes. I really did not trust it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/bookforge-done-in-one-commit.jpg&quot; alt=&quot;BookForge Done In One Commit&quot; srcset=&quot;            /assets/resized/480/bookforge-done-in-one-commit.jpg 480w,            /assets/resized/800/bookforge-done-in-one-commit.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It was essentially done with the first commit and worked well enough to be the MVP. One commit. For a complete application with multiple interfaces. What the hell is happening to software development? I have made a total of eight commits to the repo. The rest were to add docker, make slight modifications to the text in the web app, and create a release script so I can use it as a library in other projects.&lt;/p&gt;

&lt;h3 id=&quot;promptos&quot;&gt;PromptOS&lt;/h3&gt;

&lt;p&gt;A service, libraries, and extensions to store prompts and other text, markdown, and JSON instruction files for AI. This was the most complex of the four projects. Verdent broke it into 4 phases. After each phase I did a commit and approved it to start on the next phase.&lt;/p&gt;

&lt;h3 id=&quot;site-factory&quot;&gt;Site Factory&lt;/h3&gt;

&lt;p&gt;A project for building pre-configured Gatsby sites quickly. I’m still unsure of the architecture of this one and over-architected it twice. This teaches me not to use tools I haven’t used before and have AI build the project at the same time. I started over again after reading documentation on Gatsby and the other tools I planned to use to get a better idea of what I actually wanted.&lt;/p&gt;

&lt;h3 id=&quot;mostly-static&quot;&gt;Mostly Static&lt;/h3&gt;

&lt;p&gt;A set of services and dashboard for static sites. I threw this in at the last minute to put the last bit of my generous beta access to work. This was multiple phases also. But that Saturday, Verdent built two Obisidian plugins and four applications in a few hours. And did not use up the 2000 credits I got for the day.&lt;/p&gt;

&lt;h2 id=&quot;verdent-features&quot;&gt;Verdent Features&lt;/h2&gt;

&lt;p&gt;I didn’t test Verdent Deck because my personal Mac is still an Intel one and apparently I’m living in the stone age of computing.&lt;/p&gt;

&lt;h3 id=&quot;plan-mode&quot;&gt;Plan Mode&lt;/h3&gt;

&lt;p&gt;Verdent’s plan mode is where things get interesting. It will have you approve the plan before it starts building. If you’re in Auto Run mode, it will just run until it’s done with the project, though it did stop and ask about certain commands that might be destructive. Or you can go directly to Skip Permissions mode and have it stop asking question. I stuck to the middle road and only had to tell it to continue a couple of times.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-plan-mode.jpg&quot; alt=&quot;Verdent Plan Mode&quot; srcset=&quot;            /assets/resized/480/verdent-plan-mode.jpg 480w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If your directions are vague, it will ask you a series of questions to help ensure it builds what you are expecting.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-follow-up-questions.jpg&quot; alt=&quot;Verdent Follow Up Questions&quot; srcset=&quot;            /assets/resized/480/verdent-follow-up-questions.jpg 480w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For larger projects, it breaks work into phases and tells you when each is done. PromptOS was the most complex in that it had an API, website, desktop app, and extensions for two applications. Verdent broke that into 4 phases automatically. Another project I started building after this set was broken in 11 phases.&lt;/p&gt;

&lt;p&gt;You may want to tell it to save the plan to the project docs folder, so you can keep it in version control for reference, since it doesn’t do that automatically. In the newest version, you can copy the content of the plan from the chat and save it to the project if you want.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-project-plan.jpg&quot; alt=&quot;Verdent Project Plan&quot; srcset=&quot;            /assets/resized/480/verdent-project-plan.jpg 480w,            /assets/resized/800/verdent-project-plan.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The other features, to tell you the truth, I haven’t touched yet, because I simply didn’t need to.&lt;/p&gt;

&lt;h3 id=&quot;rules-system&quot;&gt;Rules System&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-rules.jpg&quot; alt=&quot;Verdent Rules&quot; srcset=&quot;            /assets/resized/480/verdent-rules.jpg 480w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The rules system lets you set custom instructions that persist across projects. This is useful for coding standards, preferred libraries, or just telling it not to do stupid shit that you’ve seen it do before.&lt;/p&gt;

&lt;h3 id=&quot;subagents&quot;&gt;Subagents&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-subagents.jpg&quot; alt=&quot;Verdent Subagents&quot; srcset=&quot;            /assets/resized/480/verdent-subagents.jpg 480w,            /assets/resized/800/verdent-subagents.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Verdent uses specialized subagents for different types of work. You don’t have to think about this much - it just routes tasks to the right AI worker automatically.&lt;/p&gt;

&lt;h3 id=&quot;mcp-support&quot;&gt;MCP Support&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/verdent-mcp-support.jpg&quot; alt=&quot;Verdent MCP Support&quot; srcset=&quot;            /assets/resized/480/verdent-mcp-support.jpg 480w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Model Context Protocol support means Verdent can integrate with other tools and services. This is probably more useful than I realize, but I haven’t had time to explore it fully given how fast everything else has been happening.&lt;/p&gt;

&lt;h2 id=&quot;the-pricing&quot;&gt;The Pricing&lt;/h2&gt;

&lt;p&gt;During the beta, I got 2000 credits a day. These were hard to use up even when I was actively trying to burn through them. Once the beta was over, I received a bucket of credits. Right now, I’m testing how long these credits last to determine how I’ll use Verdent in the future.&lt;/p&gt;

&lt;p&gt;The pricing tiers are reasonable for what you get. If you’re doing any serious development work, the cost of the tool becomes insignificant compared to the time it saves. But I’m still figuring out my usage patterns before committing to a subscription.&lt;/p&gt;

&lt;h2 id=&quot;the-verdict-too-fast-and-good-to-ignore&quot;&gt;The Verdict: Too Fast and Good to Ignore&lt;/h2&gt;

&lt;p&gt;I was definitely happy with the results. During the beta, getting through 2000 credits in a day required serious effort. The speed and quality of the output is genuinely impressive.&lt;/p&gt;

&lt;p&gt;Because I will be using it in the future. It is just too fast and good not to. Right now the only AI subscription I have is Claude Pro, so I use Claude Code mainly. But I also have API accounts at most of the big AI companies. So I might just pay for credits as I go for a while until my usage is consistent and  then pick up a subscription.&lt;/p&gt;

&lt;p&gt;The multi-AI workflow is becoming essential. When one tool is thinking, another can be building. When you have AI assistants that can complete substantial projects in under 30 minutes, the bottleneck becomes your ability to feed them work, not their ability to do it.&lt;/p&gt;

&lt;h2 id=&quot;conclusion-the-future-of-lazy-coding&quot;&gt;Conclusion: The Future of Lazy Coding&lt;/h2&gt;

&lt;p&gt;Verdent actually delivered on its promises. The difference between Verdent and some other AI coding tools I’ve used is the speed, the fact that it rarely gets confused about what I’m asking it to do, and, even though I was dreading testing the apps it built, they had less bugs and weirdness than I expected.&lt;/p&gt;

&lt;p&gt;We’re at the point where AI coding assistants are good enough that the economics start to make sense for most developers. When something can build a complete application in 30 minutes, you find a way to afford the monthly subscription.&lt;/p&gt;

&lt;p&gt;The real challenge now isn’t getting AI to write code. It’s keeping up with how fast it can work and making sure you’re feeding it projects that are actually worth building. But honestly, that’s a good problem to have.&lt;/p&gt;

&lt;p&gt;I’m still figuring out the economics of AI-assisted development, but when something works this well, you adapt. The alternative is falling behind while other developers are shipping software at warp speed.&lt;/p&gt;

&lt;p&gt;And if you’re still writing everything by hand while AI tools like Verdent exist, well, you might want to reconsider your approach. The future of coding is here, and it’s fast enough to finish your weekend projects before lunch.&lt;/p&gt;

&lt;p&gt;You can learn more about Verdent &lt;a href=&quot;https://verdent.ai/&quot;&gt;here&lt;/a&gt; and find the VS code plugin or Verdent Deck &lt;a href=&quot;https://www.verdent.ai/download&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 23 Sep 2025 08:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/verdent-ai-when-your-ai-coding-assistant-finishes-before-you-can-get-coffee/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/verdent-ai-when-your-ai-coding-assistant-finishes-before-you-can-get-coffee/</guid>
        
        
        <category>vibe-coding</category>
        
      </item>
    
      <item>
        <title>The Great Vibe Coding Experiment - How I Built 15 Projects with AI in My Spare Time</title>
        <description>&lt;p&gt;I started this whole thing wanting to test Claude Desktop with MCPs. Just one little experiment. You know how that goes.&lt;/p&gt;

&lt;p&gt;Six months later, I’ve got 15 projects in various states of “done” and a GitHub commit chart that doesn’t look too crazy until you realize that most days I only have a couple of hours to experiment with this, when I do have time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/github-commit-chart.jpg&quot; alt=&quot;Github Commit Chart&quot; srcset=&quot;            /assets/resized/480/github-commit-chart.jpg 480w,            /assets/resized/800/github-commit-chart.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Welcome to “vibe coding”: building shit because it feels right and letting AI do most of the heavy lifting. It’s not agile development. It’s not waterfall. And as I’ve done more of it, it has become let chaotic and more stuctured. Most nights I work on two projects simultaneously.&lt;/p&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#when-vibe-coding-becomes-automated-spec-driven-development&quot; id=&quot;markdown-toc-when-vibe-coding-becomes-automated-spec-driven-development&quot;&gt;When Vibe Coding Becomes Automated Spec-Driven Development&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-obsidian-plugin-empire&quot; id=&quot;markdown-toc-the-obsidian-plugin-empire&quot;&gt;The Obsidian Plugin Empire&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#apple-books-highlights-plugin-hacking-sqlite&quot; id=&quot;markdown-toc-apple-books-highlights-plugin-hacking-sqlite&quot;&gt;Apple Books Highlights Plugin: Hacking SQLite&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#joplin-portal-my-first-test-of-kiro&quot; id=&quot;markdown-toc-joplin-portal-my-first-test-of-kiro&quot;&gt;Joplin Portal: My First Test of Kiro&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#daily-note-prompts-an-extension-i-am-using&quot; id=&quot;markdown-toc-daily-note-prompts-an-extension-i-am-using&quot;&gt;Daily Note Prompts: An Extension I Am Using&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#tag-explorer-3d-testing-a-shiny-new-tool&quot; id=&quot;markdown-toc-tag-explorer-3d-testing-a-shiny-new-tool&quot;&gt;Tag Explorer 3D: Testing a Shiny New Tool&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#attachment-cleaner-simple-but-necessary&quot; id=&quot;markdown-toc-attachment-cleaner-simple-but-necessary&quot;&gt;Attachment Cleaner: Simple But Necessary&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-bigger-fish-apps-that-do-real-shit&quot; id=&quot;markdown-toc-the-bigger-fish-apps-that-do-real-shit&quot;&gt;The Bigger Fish: Apps That Do Real Shit&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#gatsby-site-with-scraper-the-abandoned-first-project&quot; id=&quot;markdown-toc-gatsby-site-with-scraper-the-abandoned-first-project&quot;&gt;Gatsby Site with Scraper: The Abandoned First Project&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#gitwrite-when-jules-met-agentic-project-manager&quot; id=&quot;markdown-toc-gitwrite-when-jules-met-agentic-project-manager&quot;&gt;GitWrite: When Jules Met Agentic Project Manager&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#embertext-my-first-claude-code-experiment&quot; id=&quot;markdown-toc-embertext-my-first-claude-code-experiment&quot;&gt;EmberText: My First Claude Code Experiment&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#mdquery-fed-up-with-proprietary-tools&quot; id=&quot;markdown-toc-mdquery-fed-up-with-proprietary-tools&quot;&gt;MDQuery: Fed Up with Proprietary Tools&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#autovibe-the-meta-project&quot; id=&quot;markdown-toc-autovibe-the-meta-project&quot;&gt;AutoVibe: The Meta Project&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#autovibe-template-the-stop-gap-solution&quot; id=&quot;markdown-toc-autovibe-template-the-stop-gap-solution&quot;&gt;AutoVibe Template: The Stop-Gap Solution&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#shopboth-testing-the-template-and-learning-hard-lessons&quot; id=&quot;markdown-toc-shopboth-testing-the-template-and-learning-hard-lessons&quot;&gt;ShopBoth: Testing the Template (and Learning Hard Lessons)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#tool-whose-name-shall-not-be-spoken&quot; id=&quot;markdown-toc-tool-whose-name-shall-not-be-spoken&quot;&gt;Tool Whose Name Shall Not Be Spoken&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-these-projects-work-together&quot; id=&quot;markdown-toc-how-these-projects-work-together&quot;&gt;How These Projects Work Together&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-i-learned-about-ai-assisted-development&quot; id=&quot;markdown-toc-what-i-learned-about-ai-assisted-development&quot;&gt;What I Learned About AI-Assisted Development&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#each-ai-tool-has-its-own-personality&quot; id=&quot;markdown-toc-each-ai-tool-has-its-own-personality&quot;&gt;Each AI Tool Has Its Own Personality&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-process-that-emerged&quot; id=&quot;markdown-toc-the-process-that-emerged&quot;&gt;The Process That Emerged&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#why-15-projects-makes-sense-sort-of&quot; id=&quot;markdown-toc-why-15-projects-makes-sense-sort-of&quot;&gt;Why 15 Projects Makes Sense (Sort Of)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#whats-next-the-ai-development-army&quot; id=&quot;markdown-toc-whats-next-the-ai-development-army&quot;&gt;What’s Next: The AI Development Army&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion-embrace-the-chaos&quot; id=&quot;markdown-toc-conclusion-embrace-the-chaos&quot;&gt;Conclusion: Embrace the Chaos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;when-vibe-coding-becomes-automated-spec-driven-development&quot;&gt;When Vibe Coding Becomes Automated Spec-Driven Development&lt;/h2&gt;

&lt;p&gt;Vibe coding is when you have an idea, fire up an AI coding assistant, and see what happens. No detailed specs. No project management software. Just “hey AI, build me a thing that does X” and then iterating until it works or you get distracted by building something else.&lt;/p&gt;

&lt;p&gt;But there was a reason I abandoned my first vibe coding project. I just added random features to an idea that I can up with on the fly and thought maybe ten minutes about. I just wanted to see what it could do. But the second project, I knew I needed some kind of rails.&lt;/p&gt;

&lt;p&gt;The next step was to get out of this loop:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tell the AI tool to create the feature&lt;/li&gt;
  &lt;li&gt;Test the result and tell the AI tool to fix the errors and failed tests&lt;/li&gt;
  &lt;li&gt;Maybe do that again or a few more times, copying and pasting errors&lt;/li&gt;
  &lt;li&gt;Ask the AI tool how to prevent this from happening with a better prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And realize it could be a self-optimizing process of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Actor: Have an agent write the code&lt;/li&gt;
  &lt;li&gt;Auditor: Have an agent review the code and run the tests (multiple types of auditor) and either pass or fail and send back to Actor&lt;/li&gt;
  &lt;li&gt;Process Improver: Have an agent examine the steps that caused the failed process and update the commands, agent definitions, or other project docs to prevent them in the future&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All because you can trust AI to do what you thought you told it to do. And this trail of projects documents my journey towards that goal.&lt;/p&gt;

&lt;h2 id=&quot;the-obsidian-plugin-empire&quot;&gt;The Obsidian Plugin Empire&lt;/h2&gt;

&lt;h3 id=&quot;apple-books-highlights-plugin-hacking-sqlite&quot;&gt;Apple Books Highlights Plugin: Hacking SQLite&lt;/h3&gt;

&lt;p&gt;I built this &lt;a href=&quot;https://github.com/eristoddle/apple-books-annotation-import&quot;&gt;plugin&lt;/a&gt; with Claude Desktop and MCPs, documented the whole process in &lt;a href=&quot;https://www.stephanmiller.com/creating-an-obsidian-plugin-with-claude/&quot;&gt;this vibe coding post&lt;/a&gt;. The plugin actually works. I use it daily. It extracts annotations from the macOS Books SQLite database and creates formatted markdown notes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-annotation-import-settings.png&quot; alt=&quot;Apple Book Annotation Import&quot; srcset=&quot;            /assets/resized/480/obsidian-annotation-import-settings.png 480w,            /assets/resized/800/obsidian-annotation-import-settings.png 800w,            /assets/resized/1400/obsidian-annotation-import-settings.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;joplin-portal-my-first-test-of-kiro&quot;&gt;Joplin Portal: My First Test of Kiro&lt;/h3&gt;

&lt;p&gt;I had all these notes in Joplin that I wanted to access from Obsidian. So I fired up Kiro AI and told it to build me &lt;a href=&quot;https://github.com/eristoddle/joplin-portal&quot;&gt;https://github.com/eristoddle/joplin-portal&lt;/a&gt;. &lt;a href=&quot;https://www.stephanmiller.com/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work/&quot;&gt;Kiro did most of the work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/joplin-portal-sidebar.jpg&quot; alt=&quot;Joplin Portal Sidebar&quot; srcset=&quot;            /assets/resized/480/joplin-portal-sidebar.jpg 480w,            /assets/resized/800/joplin-portal-sidebar.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The surprising thing about letting AI write plugins is that they actually follow best practices better than I do. Kiro created proper TypeScript interfaces, handled errors gracefully, and even added settings panels I didn’t ask for.&lt;/p&gt;

&lt;h3 id=&quot;daily-note-prompts-an-extension-i-am-using&quot;&gt;Daily Note Prompts: An Extension I Am Using&lt;/h3&gt;

&lt;p&gt;Another Kiro collaboration. This one adds customizable prompts to daily notes. I actually use this, which is more than I can say for many of the plugins I’ve tried.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-daily-prompt-settings.jpg&quot; alt=&quot;Obsidian Daily Prompt Settings&quot; srcset=&quot;            /assets/resized/480/obsidian-daily-prompt-settings.jpg 480w,            /assets/resized/800/obsidian-daily-prompt-settings.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It still needs some work, but it works for me for now. I’m just keeping a running list of changes I want to make to it and bugs I’ve run into and one of these days, I’ll put the list in one of these AI coding tools and set it to work.&lt;/p&gt;

&lt;h3 id=&quot;tag-explorer-3d-testing-a-shiny-new-tool&quot;&gt;Tag Explorer 3D: Testing a Shiny New Tool&lt;/h3&gt;

&lt;p&gt;I started this project the day after making my list of existing projects. Why? Because I wanted to test a new AI tool (can’t name it yet) and needed something to build.&lt;/p&gt;

&lt;p&gt;Tag Explorer 3D visualizes your Obsidian tags and notes with those tags in 3D space. Is it necessary? Probably not. Is it cool? Absolutely. Sometimes you build things because they’re interesting, not because they solve real problems.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-tag-explorer-3d.png&quot; alt=&quot;Obsidian Tag Explorer 3D&quot; srcset=&quot;            /assets/resized/480/obsidian-tag-explorer-3d.png 480w,            /assets/resized/800/obsidian-tag-explorer-3d.png 800w,            /assets/resized/1400/obsidian-tag-explorer-3d.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;attachment-cleaner-simple-but-necessary&quot;&gt;Attachment Cleaner: Simple But Necessary&lt;/h3&gt;

&lt;p&gt;Really simple plugin built with the same unnamed AI tool. It finds and removes unused attachments from your vault. I keep blog posts drafts there and paste images in, which gets stored there and they get forgotten when I move the draft.&lt;/p&gt;

&lt;p&gt;I need to test it more, but the code looks solid. It’s often better at the simple, boring stuff than the complex, interesting stuff if you want AI coding to work. And I may turn it into a general clean up tool, because I am also tired of tracking down conflicted files.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-cleaner.png&quot; alt=&quot;Obsidian Attachment Cleaner&quot; srcset=&quot;            /assets/resized/480/obsidian-cleaner.png 480w,            /assets/resized/800/obsidian-cleaner.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-bigger-fish-apps-that-do-real-shit&quot;&gt;The Bigger Fish: Apps That Do Real Shit&lt;/h2&gt;

&lt;p&gt;Plugins are fun, but eventually you want to build something more substantial. That’s where things got interesting.&lt;/p&gt;

&lt;h3 id=&quot;gatsby-site-with-scraper-the-abandoned-first-project&quot;&gt;Gatsby Site with Scraper: The Abandoned First Project&lt;/h3&gt;

&lt;p&gt;This was my &lt;a href=&quot;https://www.stephanmiller.com/using-mcps-with-claude-desktop/&quot;&gt;first attempt at vibe coding&lt;/a&gt;. I wanted to build a Gatsby site with an integrated scraper using Claude Desktop and MCPs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/gatsby-new-theme.png&quot; alt=&quot;AI Generated Gatsby Site&quot; srcset=&quot;            /assets/resized/480/gatsby-new-theme.png 480w,            /assets/resized/800/gatsby-new-theme.png 800w,            /assets/resized/1400/gatsby-new-theme.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I abandoned it. Not because it didn’t work, but because I realized I was building it the wrong way. Sometimes the most important decision is knowing when to stop.&lt;/p&gt;

&lt;h3 id=&quot;gitwrite-when-jules-met-agentic-project-manager&quot;&gt;GitWrite: When Jules Met Agentic Project Manager&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/eristoddle/git-write&quot;&gt;GitWrite&lt;/a&gt; is an abstraction over git for writers, editors, and beta readers. I built it with Jules AI, used an Agentic Project Manager to coordinate, and finished it up with Qoder. Well, it said it was finished and I am still working my way around to testing it’s full functionality. Just made the mistake of finishing it before I needed it.&lt;/p&gt;

&lt;p&gt;It exists to support EmberText and other writing tools I’m building. I am really, really, really tired of being required to use things like “Suggesting” in Word and Google Docs. Who thought this thing up? Satan? I like Git better but it needed dumbed down in some places and tweaked in others to do what I needed.&lt;/p&gt;

&lt;h3 id=&quot;embertext-my-first-claude-code-experiment&quot;&gt;EmberText: My First Claude Code Experiment&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/&quot;&gt;EmberText&lt;/a&gt; was my first serious relationship with Claude Code. I built an Electron app for writers, rolling my own context and project management system.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/embertext-project.png&quot; alt=&quot;EmberText Project View&quot; srcset=&quot;            /assets/resized/480/embertext-project.png 480w,            /assets/resized/800/embertext-project.png 800w,            /assets/resized/1400/embertext-project.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Claude Code has quirks. It’s opinionated about project structure. It sometimes goes off on tangents. But when it works, it does pretty well. EmberText is a fully functional app that I am testing, but I also need to refactor it to use some of the services I am building, so it is probably the last of these project that will be finished.&lt;/p&gt;

&lt;h3 id=&quot;mdquery-fed-up-with-proprietary-tools&quot;&gt;MDQuery: Fed Up with Proprietary Tools&lt;/h3&gt;

&lt;p&gt;I got tired of proprietary MCPs and tools to search systems that essentially consist of markdown files: Obsidian, Joplin, Jekyll, Log Seq, and on and on. So I built a universal tool with Kiro and Qoder.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/eristoddle/mdquery&quot;&gt;MDQuery&lt;/a&gt; provides SQL-like syntax for searching and analyzing markdown files across different note-taking systems and static site generators. Because fuck trying custom MCPs that don’t work the way you want for each and every markdown based platform.&lt;/p&gt;

&lt;p&gt;And I already had a job lined up for the tool. I had been collecting everything interesting around vibe coding and spec-driven development in my Obsidian vault under a specific tag. Developers were going in so many directions that I wanted to categorize and get an overview. We’re all blind developers describing different parts of this elephant. Maybe if we categorize what we’re all doing, I can be a little less blind. I recently found this post on &lt;a href=&quot;https://shmck.substack.com/p/claude-code-framework-wars&quot;&gt;Claude Code framework wars&lt;/a&gt; that does just that.&lt;/p&gt;

&lt;p&gt;So I attached the MCP to Claude desktop and prompted it to analyze all of my Obsidian notes, using the &lt;a href=&quot;https://github.com/eristoddle/mdquery/blob/main/docs/claude-desktop-prompts.md&quot;&gt;prompts the AI tools had put in the documentation&lt;/a&gt;. And it spit out an &lt;a href=&quot;/downloads/LlmCodingNotes.pdf&quot;&gt;80 page document&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;autovibe-the-meta-project&quot;&gt;AutoVibe: The Meta Project&lt;/h3&gt;

&lt;p&gt;AutoVibe is the most recursive project I’ve ever built. I’m using Claude Code, AI Studio, and Backlog.md to build a tool that will make the vibe coding process smoother.&lt;/p&gt;

&lt;p&gt;It’s infrastructure for building infrastructure. Custom commands and agents to coordinate AI tools. The future of development might be less about writing code and more about conducting orchestras of AI agents. At least, that’s what I think it is. But it’s like a box of chocolates.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/autovibe-dashboard.png&quot; alt=&quot;AutoVibe Dashboard&quot; srcset=&quot;            /assets/resized/480/autovibe-dashboard.png 480w,            /assets/resized/800/autovibe-dashboard.png 800w,            /assets/resized/1400/autovibe-dashboard.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;autovibe-template-the-stop-gap-solution&quot;&gt;AutoVibe Template: The Stop-Gap Solution&lt;/h3&gt;

&lt;p&gt;While AutoVibe has bugs to work out, I needed something that worked now. So I built a &lt;a href=&quot;https://github.com/eristoddle/autovibe-backlog-md-template&quot;&gt;template&lt;/a&gt; with a set of Claude commands and agents that, when used with Backlog.md and Claude Code, makes developing projects more bullet-resistant.&lt;/p&gt;

&lt;p&gt;I used Claude Desktop to help develop it since the template is full of AI instruction files that other tools would try to execute. But then I sort of got stuck testing its usage in the next project. So now the priority is getting AutoVibe to work and keep the simple template.&lt;/p&gt;

&lt;h3 id=&quot;shopboth-testing-the-template-and-learning-hard-lessons&quot;&gt;ShopBoth: Testing the Template (and Learning Hard Lessons)&lt;/h3&gt;

&lt;p&gt;I used Claude Code with my Backlog.md template to build ShopBoth, a React Native app for testing and tweaking the template. Why did I choose React Native for testing? Because I’m an idiot.&lt;/p&gt;

&lt;p&gt;I also discovered that “generic” automated QA doesn’t exist. QA needs to be specific to the platform, the framework, the use case. There ain’t no such thing as universal testing, and I learned that the hard way.&lt;/p&gt;

&lt;h2 id=&quot;tool-whose-name-shall-not-be-spoken&quot;&gt;Tool Whose Name Shall Not Be Spoken&lt;/h2&gt;

&lt;p&gt;I’ve built three more apps with an AI tool I can tell you about in a couple of weeks. They add to the ecosystem:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;BookForge&lt;/strong&gt; transforms markdown files into professional ebooks. It supports EmberText and GitWrite, completing the writing workflow.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PromptOS&lt;/strong&gt; stores and provides prompts for EmberText, AutoVibe, and everything else. Because managing prompts across 15 projects gets complicated fast.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Site Factory&lt;/strong&gt; brings me full circle to that abandoned Gatsby project, but with actual research this time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren’t random projects. They’re pieces of a larger system for AI-assisted content creation and development.&lt;/p&gt;

&lt;p&gt;Update: I actually built one more project while I was writing this. It’s a platform to provide dynamic services for static sites.&lt;/p&gt;

&lt;h2 id=&quot;how-these-projects-work-together&quot;&gt;How These Projects Work Together&lt;/h2&gt;

&lt;p&gt;This isn’t just a collection of random tools. There’s method to the madness:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;EmberText handles writing → GitWrite manages collaboration → BookForge generates ebooks&lt;/li&gt;
  &lt;li&gt;PromptOS supports everything with prompt management&lt;/li&gt;
  &lt;li&gt;Obsidian plugins feed the writing process with research and notes&lt;/li&gt;
  &lt;li&gt;MDQuery searches across all the markdown files these tools create&lt;/li&gt;
  &lt;li&gt;AutoVibe coordinates the development of new tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s an actual ecosystem, not just 15 disconnected projects. Each piece makes the others more useful.&lt;/p&gt;

&lt;h2 id=&quot;what-i-learned-about-ai-assisted-development&quot;&gt;What I Learned About AI-Assisted Development&lt;/h2&gt;

&lt;h3 id=&quot;each-ai-tool-has-its-own-personality&quot;&gt;Each AI Tool Has Its Own Personality&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Claude Desktop:&lt;/strong&gt; Great for planning and architecture, terrible for actually writing code. It’s the project manager that never writes any code. Mainly because chat lengths are limited. As soon as you get somewhere, you have to start a new chat and lose the context. I do use it to develop git templates for AI-driven coding projects, because it will ignore the instruction files I have it tweak.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Claude Code:&lt;/strong&gt; I am not sure if it is still the best after using Qoder and the new tool I have. I still use it almost daily to work on projects and it’s my goto tool, but the limit comes up quicker now and it seems like it has dropped some IQ points. Not sure about it status in my workflow right now.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Kiro:&lt;/strong&gt; The reliable workhorse. Give it a clear task and it delivers solid, working code. Perfect for plugins and smaller projects. After seeing how the new tool I found works, I wonder if it breaks things down into too many tasks. To create an Obsidian plugin, it broke it down into 16 tasks I had to click to get through and it took a few hours. The new tool just decided it would build a plugin for me in 15 minutes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Jules:&lt;/strong&gt; I still use it for small things, like fixing bugs, because I still get 15 free chats a day. I actually built most of GitWrite with it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Qoder:&lt;/strong&gt; Set it loose on a complex project and come back in a few hours to find it’s built everything you asked for and more. The wikis it build are useful but I think would eat up a lot of tokens in large codebases.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unnamed Tool:&lt;/strong&gt; The new experiment. Still figuring out its personality. But it’s fast. It finished the two Obsidian plugins in less than an hour, both of them. It built the other full projects in one day, all of them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-process-that-emerged&quot;&gt;The Process That Emerged&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Start with an actual need, not cool technology. I tried building technology first and it never worked.&lt;/li&gt;
  &lt;li&gt;Let AI handle the boilerplate and focus on the interesting problems. AI is great at CRUD operations and terrible at creative problem-solving.&lt;/li&gt;
  &lt;li&gt;Build infrastructure projects to support main projects. GitWrite exists so EmberText can focus on writing, not version control. Also, hoping less scope mean less context an AI tool needs&lt;/li&gt;
  &lt;li&gt;Test new AI tools with real projects, not demos. You learn more building something you’ll actually use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;why-15-projects-makes-sense-sort-of&quot;&gt;Why 15 Projects Makes Sense (Sort Of)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Each project taught me something new about working with different AI tools.&lt;/li&gt;
  &lt;li&gt;Building an ecosystem requires multiple pieces. You can’t do everything with one app.&lt;/li&gt;
  &lt;li&gt;Some projects exist only to support other projects. That’s fine.&lt;/li&gt;
  &lt;li&gt;Abandoning projects is part of the process. That abandoned Gatsby site taught me what not to build.&lt;/li&gt;
  &lt;li&gt;The commit chart tells the story. Bursts of activity when testing new tools. Long gaps when focusing on one complex project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;whats-next-the-ai-development-army&quot;&gt;What’s Next: The AI Development Army&lt;/h2&gt;

&lt;p&gt;AutoVibe is getting closer to making this process smoother. The template approach gives consistent results. The unnamed AI tool projects will add new capabilities.&lt;/p&gt;

&lt;p&gt;I’m building a sustainable vibe coding workflow that lets me maintain 15+ projects without losing my mind. The future of development might be more like conducting an orchestra than playing a solo instrument.&lt;/p&gt;

&lt;h2 id=&quot;conclusion-embrace-the-chaos&quot;&gt;Conclusion: Embrace the Chaos&lt;/h2&gt;

&lt;p&gt;I started wanting to try Claude Desktop with MCPs. I ended up with 15 projects, a completely new development process, and insights into how AI-assisted development actually works.&lt;/p&gt;

&lt;p&gt;The learning process matters more than perfect planning. AI tools are getting good enough to enable this kind of scattered productivity. You can afford to be curious, to follow tangents, to build infrastructure for projects that don’t exist yet.&lt;/p&gt;

&lt;p&gt;Vibe coding and spec-driven development isn’t for everyone. But if you’re comfortable with chaos (Yes, even in spec-driven development), if you like building things just to see what happens, if you want to push the boundaries of what one person can build with AI assistance, give it a try.&lt;/p&gt;

&lt;p&gt;Just don’t blame me when you end up with 15 projects and a commit chart that looks like madness. That’s the price you pay. And while I was finishing this up, I started another project. I had to give my new tool something to work on before the free ride runs out.&lt;/p&gt;

</description>
        <pubDate>Mon, 15 Sep 2025 02:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/the-great-vibe-coding-experiment/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/the-great-vibe-coding-experiment/</guid>
        
        
        <category>vibe-coding</category>
        
      </item>
    
      <item>
        <title>How I Built Two Obsidian Plugins While Kiro AI Did Most of the Work</title>
        <description>&lt;p&gt;For &lt;a href=&quot;https://www.stephanmiller.com/creating-an-obsidian-plugin-with-claude/&quot;&gt;the first Obsidian plugin I wrote with AI&lt;/a&gt;, I used Claude desktop and already had a Python script that did most of what I needed the plugin to do. I just needed AI to convert it to an Obsidian plugin. Then &lt;a href=&quot;https://www.stephanmiller.com/using-jules-to-update-my-obsidian-plugin/&quot;&gt;I used Jules to fix the final bugs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But I have been moving away from the chaos of vibe coding so I can get results that look more like the vision I have in my head. This started when &lt;a href=&quot;https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/&quot;&gt;I rolled my project management system with Claude Code and a bunch of markdown files&lt;/a&gt;. Then I found out that &lt;a href=&quot;https://www.stephanmiller.com/vibe-coding-with-backlogmd/&quot;&gt;Backlog.md could make my vibe coding projects go more smoothly&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But along the way, Kiro came out and had a spec-driven development feature. So I figured I’d try it building even more Obsidian plugins. Why two, though? Well, the first one was more complex, and the more complex a project, the more things AI leaves behind that you have to clean up. The second one was actually simple enough that I am currently prepping it to release it officially.&lt;/p&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#what-is-kiro-and-why-im-using-it&quot; id=&quot;markdown-toc-what-is-kiro-and-why-im-using-it&quot;&gt;What is Kiro and Why I’m Using It&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#plugin-1-daily-note-prompts---the-spec-driven-experience&quot; id=&quot;markdown-toc-plugin-1-daily-note-prompts---the-spec-driven-experience&quot;&gt;Plugin #1: Daily Note Prompts - The Spec-Driven Experience&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#requirement-1&quot; id=&quot;markdown-toc-requirement-1&quot;&gt;Requirement 1&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#my-obsidian-development-process&quot; id=&quot;markdown-toc-my-obsidian-development-process&quot;&gt;My Obsidian Development Process&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#plugin-2-joplin-portal---when-ai-tools-hit-a-wall&quot; id=&quot;markdown-toc-plugin-2-joplin-portal---when-ai-tools-hit-a-wall&quot;&gt;Plugin #2: Joplin Portal - When AI Tools Hit a Wall&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lessons-learned&quot; id=&quot;markdown-toc-lessons-learned&quot;&gt;Lessons Learned&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#spec-driven-development-workflow&quot; id=&quot;markdown-toc-spec-driven-development-workflow&quot;&gt;Spec-driven development workflow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#when-to-trust-ai-vs-when-to-investigate-yourself&quot; id=&quot;markdown-toc-when-to-trust-ai-vs-when-to-investigate-yourself&quot;&gt;When to trust AI vs when to investigate yourself&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#managing-ai-tool-limitations-and-daily-limits&quot; id=&quot;markdown-toc-managing-ai-tool-limitations-and-daily-limits&quot;&gt;Managing AI tool limitations and daily limits&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-value-of-simple-projects-for-learning-new-tools&quot; id=&quot;markdown-toc-the-value-of-simple-projects-for-learning-new-tools&quot;&gt;The value of simple projects for learning new tools&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-is-kiro-and-why-im-using-it&quot;&gt;What is Kiro and Why I’m Using It&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://kiro.dev/blog/introducing-kiro/&quot;&gt;Kiro&lt;/a&gt; is an AI coding tool that takes a different approach than most of the tools I’ve been testing. Instead of just throwing code at you, it starts with what they call “spec-driven development.” You give Kiro an idea, and it creates three files: requirements.md, design.md, and tasks.md.&lt;/p&gt;

&lt;p&gt;Kiro breaks down your project into digestible chunks before writing a single line of code. No more “let’s see what happens” coding sessions that end with you staring at a pile of TypeScript wondering how you got there.&lt;/p&gt;

&lt;p&gt;But here’s the real reason I’m going all-in on Kiro right now: it’s completely free. No credits, no tokens, but there is a daily limit that cuts you off for 24 hours, but I can deal with that.&lt;/p&gt;

&lt;p&gt;So I’m testing Kiro on every coding project I can think of before the free tier disappears. Obsidian plugins are perfect for this because I use Obsidian daily, I have a constant stream of plugin ideas, and I can see the results of mine and Kiro’s efforts quickly.&lt;/p&gt;

&lt;h2 id=&quot;plugin-1-daily-note-prompts---the-spec-driven-experience&quot;&gt;Plugin #1: Daily Note Prompts - The Spec-Driven Experience&lt;/h2&gt;

&lt;p&gt;I used to write daily. I had a morning practice where I woke up, meditated, read, and then wrote. But who has all that time? Actually, I’m kicking myself because I had a three-year streak, and it only took one missed day for me to give that up. The “streak” gurus never tell you about that part.&lt;/p&gt;

&lt;p&gt;And for a while, I was doing it in Obsidian using Daily Notes. Now I wanted a plugin to nag me about it and give me a prompt to start with. So that was the start of the idea. Here’s how I fleshed it out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Prompt packs in JSON format can be imported and exported with these attributes:
      - Type: ‘Sequential’, ‘Random’, ‘Date’
      - Prompt: Link, String, or Markdown
      - Date: For Date type like devotionals
      - Order: For Sequential type that have to be done in order&lt;/li&gt;
  &lt;li&gt;Set Reminder/Alert with System or Obsidian notification.&lt;/li&gt;
  &lt;li&gt;Launches daily note with prompt&lt;/li&gt;
  &lt;li&gt;Automatically go into zen mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that is basically what I gave Kiro in the first prompt. You can actually still “vibe code” with Kiro. You just have to select that option.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/kiro-chat-drawer.jpg&quot; alt=&quot;Kiro Chat Drawer&quot; srcset=&quot;            /assets/resized/480/kiro-chat-drawer.jpg 480w,            /assets/resized/800/kiro-chat-drawer.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And Kiro built the three spec files in order, waiting after each file for my approval or for me to request changes.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/eristoddle/obsidian-daily-note-prompts/blob/main/.kiro/specs/obsidian-daily-prompts/requirements.md&quot;&gt;requirements file&lt;/a&gt; has entries that look like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h3 id=&quot;requirement-1&quot;&gt;Requirement 1&lt;/h3&gt;

  &lt;p&gt;&lt;strong&gt;User Story:&lt;/strong&gt; As an Obsidian user, I want to create and manage prompt packs with different delivery modes, so that I can organize my writing prompts according to my preferred workflow.&lt;/p&gt;

  &lt;h4 id=&quot;acceptance-criteria&quot;&gt;Acceptance Criteria&lt;/h4&gt;

  &lt;ol&gt;
    &lt;li&gt;WHEN a user creates a new prompt pack THEN the system SHALL allow them to specify the type as ‘Sequential’, ‘Random’, or ‘Date’&lt;/li&gt;
    &lt;li&gt;WHEN a user adds prompts to a pack THEN the system SHALL support prompts as links, strings, or markdown content&lt;/li&gt;
    &lt;li&gt;WHEN a user creates a Sequential prompt pack THEN the system SHALL allow them to define the order of prompts&lt;/li&gt;
    &lt;li&gt;WHEN a user creates a Date-based prompt pack THEN the system SHALL allow them to assign specific dates to prompts&lt;/li&gt;
    &lt;li&gt;WHEN a user creates a Random prompt pack THEN the system SHALL randomly select prompts without repetition until all are used&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/eristoddle/obsidian-daily-note-prompts/blob/main/.kiro/specs/obsidian-daily-prompts/design.md&quot;&gt;design file&lt;/a&gt; specifies things like data models, service interfaces, and other architectural details.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/kiro-design-file.jpg&quot; alt=&quot;Kiro Design File&quot; srcset=&quot;            /assets/resized/480/kiro-design-file.jpg 480w,            /assets/resized/800/kiro-design-file.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The final file it creates is the &lt;a href=&quot;https://github.com/eristoddle/obsidian-daily-note-prompts/blob/main/.kiro/specs/obsidian-daily-prompts/tasks.md&quot;&gt;tasks file&lt;/a&gt; which look like a basic list of nested to dos in markdown:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/kiro-tasks-file.jpg&quot; alt=&quot;Kiro Tasks File&quot; srcset=&quot;            /assets/resized/480/kiro-tasks-file.jpg 480w,            /assets/resized/800/kiro-tasks-file.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But when it is loaded in the Kiro IDE, it adds a &lt;strong&gt;Start task&lt;/strong&gt; link you can click on to have Kiro to start working on it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/kiro-implementation-plan.jpg&quot; alt=&quot;Kiro Implementation Plan&quot; srcset=&quot;            /assets/resized/480/kiro-implementation-plan.jpg 480w,            /assets/resized/800/kiro-implementation-plan.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I am not sure whether adding a command to the trust list was buggy, or I just didn’t know how to use it. It would seem that you could just click the play triangle once and the double play triangle to trust the command in the future, but that didn’t seem to work all the time. Then I realized that the list of commands below the paragraph were also buttons and I had more success with those, but not every time. And like I said, it could be user error, but they didn’t make it easy to figure out. And who want’s to read documentation, anyway? No one’s got time for that.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/trusting-kiro-commands.jpg&quot; alt=&quot;Trusting Kiro Commands&quot; srcset=&quot;            /assets/resized/480/trusting-kiro-commands.jpg 480w,            /assets/resized/800/trusting-kiro-commands.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the end, I clicked &lt;strong&gt;Start task&lt;/strong&gt; over and over for about four hours while doing other things, finished all the tasks, and decided it was too late to even attempt testing it that night. I know how that goes. So, I tested the next day.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-daily-prompt-settings.jpg&quot; alt=&quot;Obsidian Daily Prompt Settings&quot; srcset=&quot;            /assets/resized/480/obsidian-daily-prompt-settings.jpg 480w,            /assets/resized/800/obsidian-daily-prompt-settings.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And it actually went pretty well. There were two or three minor bugs. For example, the time field for notification needed debouncing. It was basically working after about a half an hour of bug fixing. But there is a lot of weirdness. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It rolled its own Zen mode, and it does not work quite right. It removed the sidebars and other things. But I couldn’t figure out a way back to normal mode. I just reloaded Obsidian.&lt;/li&gt;
  &lt;li&gt;It has global settings that should be overridden by the settings of each prompt pack, but nothing happens to a prompt in a prompt pack if I don’t set the child settings. So, I think most of the global settings do nothing.&lt;/li&gt;
  &lt;li&gt;It doesn’t track progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-daily-prompt-edit-prompts.jpg&quot; alt=&quot;Obsidian Daily Prompt Edit Prompts&quot; srcset=&quot;            /assets/resized/480/obsidian-daily-prompt-edit-prompts.jpg 480w,            /assets/resized/800/obsidian-daily-prompt-edit-prompts.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I am testing it currently and making notes on the changes I need, and once I think I have them all, I’ll start working on it again. I am actually using it, but it is not finished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s the repo:&lt;/strong&gt; &lt;a href=&quot;https://github.com/eristoddle/obsidian-daily-note-prompts&quot;&gt;Obsidian Daily Note Prompts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install this plugin using the &lt;a href=&quot;https://obsidian.md/plugins?search=brat#&quot;&gt;BRAT plugin&lt;/a&gt; before I submit it.&lt;/p&gt;

&lt;p&gt;The other Obsidian plugin idea was much simpler, so I could finish that one.&lt;/p&gt;

&lt;h2 id=&quot;my-obsidian-development-process&quot;&gt;My Obsidian Development Process&lt;/h2&gt;

&lt;p&gt;After building my fourth Obsidian plugin (3 using AI), I have a process that works really well:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I will use Kiro for now while it’s free, because it’s free and I could get results with both plugins before I hit the daily limit.&lt;/li&gt;
  &lt;li&gt;I have a Test vault and keep all my plugin repos in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.obsidian/plugins&lt;/code&gt; folder of the vault.&lt;/li&gt;
  &lt;li&gt;I test the plugin in that vault and keep notes on the improvements and changes I want to make in a note in that vault rather than sending one-off prompts to Kiro.&lt;/li&gt;
  &lt;li&gt;When I think I have found enough for another spec in Kiro, I just paste what I’ve been collecting into another Kiro spec chat. A chat interface makes me anxious, to tell the truth, because it requires interaction. This allows me to deal with responding on my time. It also means I can plan changes even when I’ve been cut off.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/kiro-features-prompt.jpg&quot; alt=&quot;Kiro Features Prompt&quot; srcset=&quot;            /assets/resized/480/kiro-features-prompt.jpg 480w,            /assets/resized/800/kiro-features-prompt.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;plugin-2-joplin-portal---when-ai-tools-hit-a-wall&quot;&gt;Plugin #2: Joplin Portal - When AI Tools Hit a Wall&lt;/h2&gt;

&lt;p&gt;With this plugin, I simply wanted to access Joplin from Obsidian and be able to import notes from Joplin directly from the plugin. I know I can copy and paste the note or export notes from Joplin, but a plugin will make things easier.&lt;/p&gt;

&lt;p&gt;These are the notes I gave Kiro:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Name: Joplin Portal&lt;/li&gt;
  &lt;li&gt;A way to access my notes in Joplin from Obsidian so I don’t have to junk up my vault&lt;/li&gt;
  &lt;li&gt;Use the Joplin Web Clipper API to interact with Joplin: &lt;a href=&quot;https://joplinapp.org/help/api/references/rest_api/&quot;&gt;Joplin Data API | Joplin&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Add a sidebar panel that searches Joplin notes either by full text or by tag&lt;/li&gt;
  &lt;li&gt;Also import and convert Joplin notes as Obsidian notes (template, default folder, etc) probably using the same search functionality, with a checkbox to check if you want to import that result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project went about the same way. Kiro created the requirements, design, and task files, and I clicked &lt;strong&gt;Start task&lt;/strong&gt; over and over for 3-4 hours (while doing other things) until I discovered that there is a limit to Kiro usage and I hit it. And from what I can tell online, it’s a 24-hour break. And there was one task left, so I actually had Jules finish that task.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/joplin-portal-sidebar.jpg&quot; alt=&quot;Joplin Portal Sidebar&quot; srcset=&quot;            /assets/resized/480/joplin-portal-sidebar.jpg 480w,            /assets/resized/800/joplin-portal-sidebar.jpg 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And it worked relatively well. The biggest issue was that the plugin was pulling in the Joplin notes as is, and Joplin references images in the note with a Joplin ID. So, unlike the screenshot above, the images were not showing. So in my next Kiro session, I worked on that.&lt;/p&gt;

&lt;p&gt;In trying to fix this, I relearned a lesson about using AI for development. If it takes the tool more than two tries to fix something, provide the AI tool more information.&lt;/p&gt;

&lt;p&gt;So after about 8 tries and 2 days, I gathered the information it needed myself because depending on how the note was created in Joplin, embedded images showed up in one of three formats. Then I found the function that created the preview and the function that imported the note into Obsidian. I told Kiro about all of this, and once it had that, it fixed the issue in both functions.&lt;/p&gt;

&lt;p&gt;And it was pretty close to done, but I wanted to clean up the UI a little and give the important things a little more space. So I started another spec chat on that, and Kiro created a new set of three files called ui-improvements.&lt;/p&gt;

&lt;p&gt;Then, to prep for submitting to the community, I had it look up the rules for plugin submission and create some specs for that as well. Now that I know I can keep adding new specs as a project grows, I might try using Kiro with something bigger than an Obsidian plugin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s the repo:&lt;/strong&gt; &lt;a href=&quot;https://github.com/eristoddle/joplin-portal&quot;&gt;Obsidian Joplin Portal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install this plugin using the &lt;a href=&quot;https://obsidian.md/plugins?search=brat#&quot;&gt;BRAT plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/kiro-specs-hooks-steering-and-mcp.jpg&quot; alt=&quot;Kiro Specs, Hooks, Steering, and MCP&quot; srcset=&quot;            /assets/resized/480/kiro-specs-hooks-steering-and-mcp.jpg 480w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;

&lt;h3 id=&quot;spec-driven-development-workflow&quot;&gt;Spec-driven development workflow&lt;/h3&gt;

&lt;p&gt;Turns out there’s something to be said for planning before you code. What a concept!
The three-file system is genuinely useful. When I hit a wall or need to explain a bug to Kiro, I can reference the original specs instead of trying to reverse-engineer what sleep-deprived-me was thinking at 2 AM last Tuesday.&lt;/p&gt;

&lt;p&gt;Also, no more scope creep disguised as “quick features.” When everything is specced out upfront, it’s obvious when you’re adding random shit that doesn’t belong. That is if you pay attention instead of accepting everything as if it were only terms and conditions.&lt;/p&gt;

&lt;h3 id=&quot;when-to-trust-ai-vs-when-to-investigate-yourself&quot;&gt;When to trust AI vs when to investigate yourself&lt;/h3&gt;

&lt;p&gt;Give any AI tool exactly two chances to fix a complex problem. After that, you’re just feeding prompts to a very expensive random number generator.&lt;/p&gt;

&lt;p&gt;The Joplin image rendering issue taught me this lesson yet again. I spent hours watching different AI tools chase their own tails, generating increasingly elaborate solutions to a problem they didn’t understand. When I all I had to do was stop, investigate what was happening for less than an hour, and give Kiro the details.&lt;/p&gt;

&lt;p&gt;AI tools are great at implementing solutions. They’re terrible at debugging complex integration issues that require understanding context they don’t have access to. It remains to be seen when I will actually remember this earlier.&lt;/p&gt;

&lt;h3 id=&quot;managing-ai-tool-limitations-and-daily-limits&quot;&gt;Managing AI tool limitations and daily limits&lt;/h3&gt;

&lt;p&gt;Treat daily limits like sprint deadlines. Plan your work in chunks that can be completed within the limit, and always end each session by documenting exactly where you are. Nothing sucks more than hitting your limit mid-debugging session and forgetting what the hell you were trying to fix.&lt;/p&gt;

&lt;p&gt;Also, don’t game the system by splitting complex tasks into tiny prompts. You’ll just confuse the AI and waste your allocation on back-and-forth clarifications.&lt;/p&gt;

&lt;h3 id=&quot;the-value-of-simple-projects-for-learning-new-tools&quot;&gt;The value of simple projects for learning new tools&lt;/h3&gt;

&lt;p&gt;Complex projects are terrible for learning new AI tools. You spend too much time fighting edge cases and not enough time understanding the tool’s strengths and weaknesses. The Joplin Portal plugin was simple enough that I could focus on Kiro’s workflow instead of drowning in business logic. The Daily Note Prompts plugin had a more going on.&lt;/p&gt;

&lt;p&gt;Start simple. Learn the tool. Then tackle the hard stuff when you’re not also learning how to communicate with an AI that may or may not understand what a TypeScript interface is. Simple projects also fail faster and more obviously, which means you spend less time wondering if the AI is broken or if your requirements were just garbage to begin with.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Kiro’s free tier is basically a coding assistant on steroids with no monthly subscription guilt. While it lasts, I’m going to get my money’s worth, even though it’s not costing me any money.&lt;/p&gt;

&lt;p&gt;The spec-driven approach works well. Having an AI that plans before it codes instead of just eyeballing everything and hoping for the best is a game changer. But… I was already building my own tool to do this before I heard of Kiro, so I know it’s not “magic.” And I also suspect that it consists mainly of system prompts.&lt;/p&gt;

&lt;p&gt;But while it’s free… So if you see a bunch of random Obsidian plugins with my name on them over the next few months, you’ll know why. I’m not building them because the world desperately needs another note-taking plugin. I’m building them because there’s a free AI coding assistant that won’t be free forever, and I plan to learn everything I can while the learning is cheap.&lt;/p&gt;

&lt;p&gt;And while I get side-tracked and my own AI software is not ready yet, my next post should be about using AI to build that software. The process I have works really well, and I am building a public GitHub template repository to recreate it.&lt;/p&gt;
</description>
        <pubDate>Fri, 22 Aug 2025 02:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work/</guid>
        
        
        <category>vibe-coding</category>
        
        <category>obsidian</category>
        
        <category>javascript</category>
        
      </item>
    
      <item>
        <title>I Tried to Upgrade My Blog with AI Project Management and Everything Went to Hell (But the Process Worked Really Well)</title>
        <description>&lt;p&gt;Shortly after I started &lt;a href=&quot;https://www.stephanmiller.com/using-mcps-with-claude-desktop/&quot;&gt;my first vibe coding project&lt;/a&gt;, I knew I took a wrong turn. There was no way I was going to be able to work on a larger project without planning and there was no way an AI tool was going to stick to a plan without a system. And I sort of abandoned that first project. If I do start on it again, I will start better.&lt;/p&gt;

&lt;p&gt;Claude Code had at least some sort of guard rails when it came out, the CLAUDE.md file, but a few days into &lt;a href=&quot;https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/&quot;&gt;building an Electron app with Claude Code&lt;/a&gt;, I knew I needed more. So I rolled my own system using more markdown files. A file per feature and a reference to the file in the CLAUDE.md file. I am still working on this app using that system.&lt;/p&gt;

&lt;p&gt;But while I was doing all of this coding work, I was looking at what other people were doing. I discovered I was at least going in the right direction, which is a good feeling. I also found some tools that were close to what I want to build, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/superbasicstudio/claude-conductor&quot;&gt;GitHub - superbasicstudio/claude-conductor: Claude Conductor - a simple Claude Code framework&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/MrLesk/Backlog.md&quot;&gt;GitHub - MrLesk/Backlog.md: Backlog.md - A tool for managing project collaboration between humans and AI Agents in a git ecosystem&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/SuperClaude-Org/SuperClaude_Framework&quot;&gt;GitHub - SuperClaude-Org/SuperClaude_Framework: A configuration framework that enhances Claude Code with specialized commands, cognitive personas, and development methodologies.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Jedward23/Tmux-Orchestrator&quot;&gt;GitHub - Jedward23/Tmux-Orchestrator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/anubissbe/ProjectHub-Mcp&quot;&gt;GitHub - anubissbe/ProjectHub-Mcp: 🚀 Enterprise-grade task management web interface with time tracking, workflow templates, dependency visualization, team collaboration, and analytics dashboard. Built with React, TypeScript, and PostgreSQL.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/BloopAI/vibe-kanban?tab=readme-ov-file&quot;&gt;GitHub - BloopAI/vibe-kanban: Kanban board to manage your AI coding agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#backlogmd-the-tool-that-actually-made-sense&quot; id=&quot;markdown-toc-backlogmd-the-tool-that-actually-made-sense&quot;&gt;Backlog.md: The Tool That Actually Made Sense&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#from-zero-to-kanban-in-30-seconds&quot; id=&quot;markdown-toc-from-zero-to-kanban-in-30-seconds&quot;&gt;From Zero to Kanban in 30 Seconds&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-nexttask-prompt-teaching-claude-to-be-your-project-manager&quot; id=&quot;markdown-toc-the-nexttask-prompt-teaching-claude-to-be-your-project-manager&quot;&gt;The /nexttask Prompt: Teaching Claude to Be Your Project Manager&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#smooth-sailingwith-caveats&quot; id=&quot;markdown-toc-smooth-sailingwith-caveats&quot;&gt;Smooth Sailing…With Caveats&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ai-musical-chairs-when-claude-credits-run-out-mid-upgrade&quot; id=&quot;markdown-toc-ai-musical-chairs-when-claude-credits-run-out-mid-upgrade&quot;&gt;AI Musical Chairs: When Claude Credits Run Out Mid-Upgrade&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-backlogmd-actually-works&quot; id=&quot;markdown-toc-why-backlogmd-actually-works&quot;&gt;Why Backlog.md Actually Works&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-backlogmd-workflow-that-actually-works&quot; id=&quot;markdown-toc-the-backlogmd-workflow-that-actually-works&quot;&gt;The Backlog.md Workflow That Actually Works&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-i-rolled-back-anyway&quot; id=&quot;markdown-toc-why-i-rolled-back-anyway&quot;&gt;Why I Rolled Back Anyway&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lessons-learned-when-success-feels-like-failure&quot; id=&quot;markdown-toc-lessons-learned-when-success-feels-like-failure&quot;&gt;Lessons Learned: When Success Feels Like Failure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;backlogmd-the-tool-that-actually-made-sense&quot;&gt;Backlog.md: The Tool That Actually Made Sense&lt;/h2&gt;

&lt;p&gt;After looking at all those options, I kept coming back to &lt;a href=&quot;https://github.com/MrLesk/Backlog.md&quot;&gt;Backlog.md&lt;/a&gt;. Unlike the other tools that seemed to be reinventing project management or adding layers of complexity, this one felt like it was solving the exact problem I had.&lt;/p&gt;

&lt;p&gt;The promise was simple: markdown-native tasks where you manage every issue as a plain .md file, plus an AI-ready CLI where you can literally tell Claude to “take over task 33.” After months of fighting with project management that lived outside my actual code, having everything in Git sounded like what I needed.&lt;/p&gt;

&lt;p&gt;What made Backlog.md different from my adhoc Claude Code system was the structure. Instead of me manually creating feature files and maintaining references in CLAUDE.md, Backlog.md turns any folder with a Git repo into a self-contained project board powered by plain markdown files and a zero-config CLI. Each task becomes its own markdown file with a naming convention that actually makes sense: task-[task-id] - [task-title].md (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task-12 - Fix typo.md&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Instead of maintaining a growing CLAUDE.md file with references to feature files, I’d get persistent task cards that AI tools could pick up individually. No more context bloat, no more losing track of what was planned versus what was implemented.&lt;/p&gt;

&lt;p&gt;The installation options are simple: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm i -g backlog.md&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bun add -g backlog.md&lt;/code&gt;, plus Homebrew if you want. Plus, 100% private and offline operation with the backlog living entirely inside your repo. No external dependencies or vendor lock-in.&lt;/p&gt;

&lt;p&gt;I decided to test this theory on the perfect guinea pig: this ancient Jekyll blog that I’d been afraid to touch for years, mainly because I knew what a rabbit hole that can be.&lt;/p&gt;

&lt;h2 id=&quot;from-zero-to-kanban-in-30-seconds&quot;&gt;From Zero to Kanban in 30 Seconds&lt;/h2&gt;

&lt;p&gt;Installing Backlog.md with bun was easy:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bun add &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; backlog.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once that finished, I navigated to my blog’s folder and ran:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;backlog init &lt;span class=&quot;s2&quot;&gt;&quot;Blog Upgrade Project&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is where I knew I chose the right tool. The zero-config CLI wasn’t marketing bullshit. It actually just worked. The init command created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.backlog&lt;/code&gt; folder in my project with a basic configuration file.&lt;/p&gt;

&lt;p&gt;Then it was time to create the files for the AI tools I was going to use.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/select-agent-files.jpeg&quot; alt=&quot;Select Agent Files to Create&quot; srcset=&quot;            /assets/resized/480/select-agent-files.jpeg 480w,            /assets/resized/800/select-agent-files.jpeg 800w,            /assets/resized/1400/select-agent-files.jpeg 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I had it create one for Claude Code and one for Gemini CLI.&lt;/p&gt;

&lt;p&gt;And I knew I could create a task manually, like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;backlog task create &lt;span class=&quot;s2&quot;&gt;&quot;Upgrade this ancient Jekyll blog&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But I wanted to use the web interface for a visual. It pretty basic but it works well and gets the job done. The only issue I had was that you have to remember to refresh the page after your AI tool updates task, but that was really no biggie.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/edit-backlogmd-task.jpeg&quot; alt=&quot;Edit Backlog.md Tasks&quot; srcset=&quot;            /assets/resized/480/edit-backlogmd-task.jpeg 480w,            /assets/resized/800/edit-backlogmd-task.jpeg 800w,            /assets/resized/1400/edit-backlogmd-task.jpeg 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Run this command to launch the web app:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;backlog browser
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/backlogmd-kanban-view.jpeg&quot; alt=&quot;Backlog.md Kanban View&quot; srcset=&quot;            /assets/resized/480/backlogmd-kanban-view.jpeg 480w,            /assets/resized/800/backlogmd-kanban-view.jpeg 800w,            /assets/resized/1400/backlogmd-kanban-view.jpeg 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I could also run:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;backlog board view
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A kanban board appeared right in my terminal, showing my single task in the “To Do” column. It was basic, but seeing that visual representation made something click. This was project management that lived with my code and could scale as simply as creating more markdown files.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/backlogmd-terminal-view.jpeg&quot; alt=&quot;Backlog.md Terminal View&quot; srcset=&quot;            /assets/resized/480/backlogmd-terminal-view.jpeg 480w,            /assets/resized/800/backlogmd-terminal-view.jpeg 800w,            /assets/resized/1400/backlogmd-terminal-view.jpeg 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Everything was just files in my repo. I could version control my project management, branch it with my code, and most importantly, any AI agent could understand the entire project context by reading well-structured markdown files instead of parsing through massive CLAUDE.md files or chat histories.&lt;/p&gt;

&lt;h2 id=&quot;the-nexttask-prompt-teaching-claude-to-be-your-project-manager&quot;&gt;The /nexttask Prompt: Teaching Claude to Be Your Project Manager&lt;/h2&gt;

&lt;p&gt;I had only recently started creating custom Claude Commands instead of typing a different prompt every time. I figured since I had a structured way of handling tasks, I should create a command to get Claude Code to work on the next task. It was pretty simple:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Analyze the next task by priority and dependencies then sort order in the TODO column. Analyze the project to determine the scope. If the task is the right size for one task, move it to doing and start working on it. When it is done, mark it as done and add a “Needs Review” label. Do not make a git commit. If the task is too large, break it up into new tasks and add them as a dependency to the original task, then stop and tell me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have since made this command much longer and now have the agent create a git branch for each new task it started working on, but this command got the job done. I would review the tickets that were tagged with “Needs Review” and then just archive the tickets after I checked they were actually done.&lt;/p&gt;

&lt;p&gt;This was the one task I created (I will find out at then end everything that I should have made this more detailed):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This is a Jekyll site that is 15 years old. The styles it uses are outdated, but I like the look for the most part. It is even relatively responsive. Create a new branch and work on a theme updated to to modern standards. I like that my current theme is plain like reading from paper.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first time I ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nexttask&lt;/code&gt; command, it split the ticket into 7 new tickets and updated it and appended this:&lt;/p&gt;

&lt;div class=&quot;language-md highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gs&quot;&gt;**Status**&lt;/span&gt;: This task has been broken down into specific subtasks (task-2 through task-7) that should be completed in sequence. Each subtask focuses on a specific aspect of the modernization:
&lt;span class=&quot;p&quot;&gt;
1.&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**task-2**&lt;/span&gt;: Update dependencies (Font Awesome, jQuery)
&lt;span class=&quot;p&quot;&gt;
2.&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**task-3**&lt;/span&gt;: Migrate grid system from Bourbon/Neat to CSS Grid/Flexbox
&lt;span class=&quot;p&quot;&gt;
3.&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**task-4**&lt;/span&gt;: Modernize build system (replace Grunt)
&lt;span class=&quot;p&quot;&gt;
4.&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**task-5**&lt;/span&gt;: Implement CSS custom properties and modern CSS features
&lt;span class=&quot;p&quot;&gt;
5.&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**task-6**&lt;/span&gt;: Performance optimization and asset loading
&lt;span class=&quot;p&quot;&gt;
6.&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**task-7**&lt;/span&gt;: Add dark mode and accessibility improvements

&lt;span class=&quot;gu&quot;&gt;## Notes&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;
-&lt;/span&gt; New branch &lt;span class=&quot;sb&quot;&gt;`ui-modernization`&lt;/span&gt; has been created for this work
&lt;span class=&quot;p&quot;&gt;
-&lt;/span&gt; Current analysis shows the site uses Skinny Bones theme with outdated Bourbon/Neat grid system
&lt;span class=&quot;p&quot;&gt;
-&lt;/span&gt; Focus should be on preserving the clean, paper-like reading experience while modernizing the underlying technology
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I didn’t expect the amount of tickets this command would eventually generate though, because it would hit one of these tickets and break it done further. The process worked, but I was never quite sure when it was going to end…until the end. By then, it had created 29 tasks.&lt;/p&gt;

&lt;h2 id=&quot;smooth-sailingwith-caveats&quot;&gt;Smooth Sailing…With Caveats&lt;/h2&gt;

&lt;p&gt;The first task Claude picked up was “Update Dependencies - Font Awesome and jQuery” and it went well. Like, suspiciously well. It identified the outdated versions, updated the package references, tested the build, and marked the task as done. I started getting cocky thinking this whole process was going to be easy, like I alway do in the beginning.&lt;/p&gt;

&lt;p&gt;Then we hit the CSS Grid migration and reality came crashing back down. What started as “Migrate from Bourbon/Neat to CSS Grid and Flexbox” immediately spawned three more subtasks when Claude realized the scope. Those three subtasks each spawned their own subtasks.&lt;/p&gt;

&lt;p&gt;By the time we were deep into the UI changes, I was hitting about a 50% success rate. Half the time Claude would implement something perfectly and move on. The other half, I’d test the changes and find broken layouts and missing responsive behavior.&lt;/p&gt;

&lt;p&gt;But I was used to this when vibe coding. Builds and CSS aren’t fun, and they’re definitely not the kind of work that AIs handle gracefully every time. The difference was that instead of losing track of what was broken in a long chat thread, I had persistent task cards showing exactly what needed fixing and what had already been tested.&lt;/p&gt;

&lt;h2 id=&quot;ai-musical-chairs-when-claude-credits-run-out-mid-upgrade&quot;&gt;AI Musical Chairs: When Claude Credits Run Out Mid-Upgrade&lt;/h2&gt;

&lt;p&gt;Right in the middle of debugging a responsive layout issue, I got the message from Claude Code that I was about to be cut off for about 3 hours or so. Perfect timing!&lt;/p&gt;

&lt;p&gt;So I decided I’d give Gemini CLI a try. The Pro model actually did pretty well at first, but since I am on the free tier there, that did not last very long. So it wasn’t long before the Flash model took over and starting breaking things and making obvious mistakes.&lt;/p&gt;

&lt;p&gt;But it was about dinner time, so I gave up and that and came back when I could use Claude Code again. I missed the workflow I could use there and that I could tell it to “think harder” when I knew it was stuck. But Backlog.md still kept track of things, so it wasn’t hard to pick where I left off.&lt;/p&gt;

&lt;h2 id=&quot;why-backlogmd-actually-works&quot;&gt;Why Backlog.md Actually Works&lt;/h2&gt;

&lt;p&gt;After trying three different approaches to AI-assisted project management, Backlog.md finally clicked because it solved the fundamental problem: persistence without complexity.&lt;/p&gt;

&lt;p&gt;With Claude Desktop and MCPs, I was constantly losing context when conversations got too long or when I had to start new chats. Every time I came back to a project, I had to re-explain where I was and what I was trying to accomplish. It was like having amnesia every few hours.&lt;/p&gt;

&lt;p&gt;Claude Code’s CLAUDE.md files were better, but they became unwieldy fast. By the time I had detailed feature plans, code style guidelines, and project status all crammed into one file, it was thousands of lines of text that Claude had to parse every time. Plus, managing references to separate feature files was manual work that I inevitably screwed up.&lt;/p&gt;

&lt;p&gt;Backlog.md gives you the power of having prompts as persistent cards instead of buried in chat histories or monolithic project files. Each task is self-contained with its own context, acceptance criteria, and history. When an AI picks up a task, it gets exactly the information it needs without wading through everything else. And since everything lives in your repository, there are no external dependencies to break or expire.&lt;/p&gt;

&lt;h3 id=&quot;the-backlogmd-workflow-that-actually-works&quot;&gt;The Backlog.md Workflow That Actually Works&lt;/h3&gt;

&lt;p&gt;Here’s the simple process that emerged:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Create tasks&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog task create &quot;Fix responsive header&quot;&lt;/code&gt; generates a markdown file with all the context&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;View progress&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog board view&lt;/code&gt; shows your terminal kanban board instantly&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hand off to AI&lt;/strong&gt;: Point Claude/Gemini at specific task files instead of explaining everything&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Track changes&lt;/strong&gt;: Git automatically versions your project management along with your code&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Review visually&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog browser&lt;/code&gt; launches a web interface when you want something prettier than terminal output&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Key commands I used constantly:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog task create&lt;/code&gt; - Generate new task files&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog board view&lt;/code&gt; - Terminal kanban board&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog browser&lt;/code&gt; - Web-based project view&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backlog task edit &amp;lt;id&amp;gt;&lt;/code&gt; - Update task details without hunting for files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-i-rolled-back-anyway&quot;&gt;Why I Rolled Back Anyway&lt;/h2&gt;

&lt;p&gt;Just because I thought something was worth doing doesn’t mean it was the best way to do it. The blog upgrade process worked perfectly, but the end result was objectively worse than what I started with. I really just wanted to remove old styles that used floats and other ancient methods and replace them with things like flexbox. The truth is, I forgot how to use some of the old styles. But I got carried away when Claude suggested all the changes. I thought, “Might as well.”&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The old Docker images was 300 mb. The new one was 500 mb.&lt;/li&gt;
  &lt;li&gt;The build time doubled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final straw was the JavaScript heap out of memory errors that started appearing during the watch process. I thought about it. I could keep trying to get this new build to work right or I could roll back. I rolled back. But at least I learned some things from the process.&lt;/p&gt;

&lt;h2 id=&quot;lessons-learned-when-success-feels-like-failure&quot;&gt;Lessons Learned: When Success Feels Like Failure&lt;/h2&gt;

&lt;p&gt;A “failed” upgrade taught me more than a successful one would have because failure forces you to examine your assumptions. I learned that just because you can modernize something doesn’t mean you should, and that the process of building something can be more valuable than the thing you build.&lt;/p&gt;

&lt;p&gt;The evolution from Claude Desktop to Claude Code to Backlog.md shows how AI project management is still figuring itself out. We’re all just throwing stuff at the wall to see what sticks. But Backlog.md actually stuck, which is more than I can say for most of the “revolutionary” AI tools that get hyped every week.&lt;/p&gt;

&lt;p&gt;Proper task management changes AI-assisted coding by giving both you and the AI clear context about what needs to be done, what’s been tried, and what’s working. Instead of the AI guessing what you want based on a conversation, it can read explicit requirements and build exactly what’s specified.&lt;/p&gt;

&lt;p&gt;I’m keeping Backlog.md in my toolkit even though this project was a disaster. Sometimes the best recommendations come from watching something work perfectly while everything else falls apart around it. It also got updated while I was working on this article and has new features, including new commands and more functionality in the web app. The tool is actively being developed, which is always a good sign for something you’re planning to rely on.&lt;/p&gt;
</description>
        <pubDate>Tue, 05 Aug 2025 02:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/vibe-coding-with-backlogmd/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/vibe-coding-with-backlogmd/</guid>
        
        
        <category>vibe-coding</category>
        
        <category>jekyll</category>
        
      </item>
    
      <item>
        <title>Jules AI - The (Currently) Free Coding Assistant That Can&apos;t Follow Directions But Gets Shit Done</title>
        <description>&lt;p&gt;Jules is currently free, so I figured it was worth a try. Of course, I had to find out what I could break.&lt;/p&gt;

&lt;p&gt;Each user gets these default limits:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;5 concurrent tasks&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;60 total tasks per day&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;5 codecasts per day&lt;/strong&gt; (still not sure what this means)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sixty tasks a day for free? That’s really generous and it meant I didn’t have to worry about reading documentation or instructions the first time.&lt;/p&gt;

&lt;h2 id=&quot;i-jumped-in-feet-first&quot;&gt;I Jumped in Feet First&lt;/h2&gt;

&lt;p&gt;My Obsidian plugin worked well enough for me to use it, but there were issues. Issues that I’d been ignoring because the damn thing worked and I had other shit to do. But I finally added issues to my GitHub repo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-plugin-github-issues.png&quot; alt=&quot;obsidian-plugin-github-issues&quot; srcset=&quot;            /assets/resized/480/obsidian-plugin-github-issues.png 480w,            /assets/resized/800/obsidian-plugin-github-issues.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Four issues. Nothing too crazy. Should be straightforward for an AI that can supposedly code.&lt;/p&gt;

&lt;p&gt;I gave Jules access to the repo and told it: “I want you to work on the issues in this repository and create a feature branch for each fix. Start with the first one.”&lt;/p&gt;

&lt;p&gt;Simple enough, right?&lt;/p&gt;

&lt;h2 id=&quot;jules-decides-to-ignore-my-instructions&quot;&gt;Jules Decides to Ignore My Instructions&lt;/h2&gt;

&lt;p&gt;Jules fetched the repo, cloned it into a VM, and read the README file. But then it discovered the “Future Enhancements” section I’d added to the README and decided that’s what it wanted to work on instead.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-plugin-future-enhancements.png&quot; alt=&quot;obsidian-plugin-future-enhancements&quot; srcset=&quot;            /assets/resized/480/obsidian-plugin-future-enhancements.png 480w,            /assets/resized/800/obsidian-plugin-future-enhancements.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It completely ignored my instruction to work on the issues, the same issues that are listed right above the “Future Enhancements” section, and decided it would work on the “Interactive book selection dialog” instead.&lt;/p&gt;

&lt;p&gt;Jules then asked me to approve the plan, but I didn’t see any way to reject it. It was going to auto-approve in about a minute anyway. I thought maybe clicking “Approve” would launch a modal where I could say “no, you illiterate AI, work on the issues I told you to work on.”&lt;/p&gt;

&lt;p&gt;It did not.&lt;/p&gt;

&lt;p&gt;So it started working on the wrong thing. I messaged “stop” and it replied: “I have stopped the current task. Is there something specific you would like me to do instead, or a different issue you’d like me to focus on from the README?”&lt;/p&gt;

&lt;p&gt;It paused for a few seconds and then continued doing exactly what it was doing before.&lt;/p&gt;

&lt;p&gt;Currently, as far as I can tell, the only way to reject a plan is to let it start, pause it manually, and then delete the entire task. User experience design at its finest.&lt;/p&gt;

&lt;p&gt;Jules finished the feature and gave me the option to click a button to create a feature branch. So now I had a new feature to test before I could fix the actual issues I wanted fixed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/jules-obsidian-plugin-branch.png&quot; alt=&quot;jules-obsidian-plugin-branch&quot; srcset=&quot;            /assets/resized/480/jules-obsidian-plugin-branch.png 480w,            /assets/resized/800/jules-obsidian-plugin-branch.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;testing-the-feature-that-jules-built-while-ignoring-my-prompt&quot;&gt;Testing the Feature that Jules Built While Ignoring My Prompt&lt;/h2&gt;

&lt;p&gt;So I pulled the branch down to the Obsidian vault I use for testing, the one with all the plugins I’m developing loaded up. There was one error in the code, in unit tests I never asked Jules to add:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tests/main.test.ts:160:35 - error TS2554: Expected 0 arguments, but got 1.
sortAnnotationsByCFI: jest.fn((ann: Annotation[]) =&amp;gt; ann), // Simple pass-through
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I just deleted the broken test.&lt;/p&gt;

&lt;p&gt;It took me a while to figure out that the selection modal didn’t launch via the sidebar button like I expected. I had to hit Command+P and find the command to launch it. But you know what? It actually worked.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/ebook-selection-modal.png&quot; alt=&quot;ebook-selection-modal&quot; srcset=&quot;            /assets/resized/480/ebook-selection-modal.png 480w,            /assets/resized/800/ebook-selection-modal.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Since the feature worked, I merged the change and created a new release. Then I did some reading to figure out how to prevent Jules from going off on another tangent.&lt;/p&gt;

&lt;h2 id=&quot;back-to-fixing-bugs-this-time-with-feeling&quot;&gt;Back to Fixing Bugs (This Time With Feeling)&lt;/h2&gt;

&lt;p&gt;I thought maybe I should be more descriptive, but there wasn’t much to be descriptive about. There are issues. I added them to the repo. They’re also in the README that I know Jules can access. Most of the &lt;a href=&quot;https://github.com/google-labs-code/jules-awesome-list&quot;&gt;example prompts&lt;/a&gt; are very short, shorter than what I’d already tried.&lt;/p&gt;

&lt;p&gt;So I tried again: “This github repo has 4 issues listed here: https://github.com/eristoddle/apple-books-annotation-import/issues. Give me a plan for fixing the first issue. If you have no access to these, let me know. Do not do anything else.”&lt;/p&gt;

&lt;p&gt;This time it worked.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/jule-web-interface.png&quot; alt=&quot;jule-web-interface&quot; srcset=&quot;            /assets/resized/480/jule-web-interface.png 480w,            /assets/resized/800/jule-web-interface.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/jules-cover-fix-plan.png&quot; alt=&quot;jules-cover-fix-plan&quot; srcset=&quot;            /assets/resized/480/jules-cover-fix-plan.png 480w,            /assets/resized/800/jules-cover-fix-plan.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Both the accidental feature and the bug fix took very little time. I was actually surprised by the speed. But Jules created broken unit tests again. I figured maybe it was because I didn’t have my npm and node versions in the package.json, so I added those and deleted them again.&lt;/p&gt;

&lt;p&gt;It actually fixed the bug and at least doubled the number of book covers I was getting.&lt;/p&gt;

&lt;h2 id=&quot;the-great-color-mapping-mystery&quot;&gt;The Great Color Mapping Mystery&lt;/h2&gt;

&lt;p&gt;The second issue was about finding annotation colors.&lt;/p&gt;

&lt;p&gt;Me: “Fix this feature in the repo: https://github.com/eristoddle/apple-books-annotation-import/issues/5. Do not do anything else.”&lt;/p&gt;

&lt;p&gt;I think “Do not do anything else” will be part of my prompt from now on.&lt;/p&gt;

&lt;p&gt;Jules said: “The code changes have been reviewed for syntactical correctness and logical soundness. Direct testing with an Apple Books database is not possible, so final validation will be up to the user.”&lt;/p&gt;

&lt;p&gt;Translation: “I changed some code and it compiles. Good luck!”&lt;/p&gt;

&lt;p&gt;After testing, I told Jules: “Now it seems like there are no annotation color indicators at all in the md files. Before they were all just purple. Now they are just not there at all.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/jules-annotation-color-message-two.png&quot; alt=&quot;jules-annotation-color-message-two&quot; srcset=&quot;            /assets/resized/480/jules-annotation-color-message-two.png 480w,            /assets/resized/800/jules-annotation-color-message-two.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The second attempt produced different colors, but they didn’t match the highlight colors in the Books app.&lt;/p&gt;

&lt;p&gt;I mapped the colors Jules gave me to the actual colors and told it about the differences: “The color images are back and there are a variety of them, but they do not match the colors in the Books app. Here is how they differ (Books app color → markdown color): underline → both yellow and underline, purple → no icon at all, pink → red, yellow → purple, blue → blue, green → green.”&lt;/p&gt;

&lt;p&gt;I even included the log showing the distinct annotation style values from my test database. So this bug took three interactions, but Jules finally got the color mapping right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-annotation-colors.png&quot; alt=&quot;obsidian-annotation-colors&quot; srcset=&quot;            /assets/resized/480/obsidian-annotation-colors.png 480w,            /assets/resized/800/obsidian-annotation-colors.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;taking-it-to-the-big-leagues&quot;&gt;Taking It to the Big Leagues&lt;/h2&gt;

&lt;p&gt;My main Apple Books account has 4000 books and over 60 books with highlights. When I first worked on this plugin, the database structure was different, so I wanted to make sure everything worked in the wild before calling it done.&lt;/p&gt;

&lt;p&gt;The plugin handled the larger database just fine. All the covers loaded correctly, and the color annotations matched what I saw in the Books app.&lt;/p&gt;

&lt;h2 id=&quot;everything-i-fixed-in-this-change&quot;&gt;Everything I Fixed In This Change&lt;/h2&gt;

&lt;p&gt;It took about two hours total, not counting the issue Jules couldn’t fix. I let it code while I took notes for this article, worked on Obsidian cleanup, and checked out Reddit for a while.&lt;/p&gt;

&lt;p&gt;Here’s what got implemented:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ New interactive ebook selection dialog:&lt;/strong&gt; By accident when I was asking Jules to fix issues, but I’m counting it as a win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Fixed finding ebook covers:&lt;/strong&gt; This was the original first issue. Jules doubled the number of covers the plugin could find.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Fixed annotation color icons:&lt;/strong&gt; Took three tries and some detailed feedback, but now the colors match what’s in the Books app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Fixed missing annotation dates:&lt;/strong&gt; This needed a second attempt. I had to tell Jules: “I have include dates set to true and include citations set to false. There are no dates in the markdown files though.” But it got it right the second time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Did not fix gibberish in epub chapter names:&lt;/strong&gt; This one turned into a nightmare. The TypeScript build error migrated from the tests that never worked. I tried to get Jules to fix it once and it failed. The second time it was thinking forever, so I tried Copilot in VS Code, and that fixed the build.&lt;/p&gt;

&lt;p&gt;But then there was a runtime error I’d run into before when Claude Code tried to use a Node.js ebook parsing package. Something about not being able to access the file system in Electron. I went round and round with Jules on this one and finally gave up.&lt;/p&gt;

&lt;p&gt;One thing I realized is that I could tell Jules to build the app to test the build and fix issues, but I had to make it a command. When I asked “Did you build the app to test it?” it said that wasn’t possible. But when I just told it to build the project, it did it.&lt;/p&gt;

&lt;p&gt;So I let it churn through fixing the build errors for 30 minutes. When I tested the result, the chapters still weren’t fixed. But I didn’t expect this one to be easy without doing some research first.&lt;/p&gt;

&lt;p&gt;I used 5 of my available 60 free tasks for the day.&lt;/p&gt;

&lt;h2 id=&quot;jules-fast-confused-but-generally-useful&quot;&gt;Jules: Fast, Confused, but Generally Useful&lt;/h2&gt;

&lt;p&gt;Jules is fast. That surprised me. At least until there’s back and forth on changes, then the conversation slows down.&lt;/p&gt;

&lt;p&gt;It was done with the next fix before I finished testing the previous one. I added one feature I didn’t plan on and fixed four issues in a little over an hour.&lt;/p&gt;

&lt;p&gt;But Jules went off on a weird tangent when I gave it what I thought were specific instructions. There seems to be no real interaction other than the first message and accepting the plan. I was worried about fixing issues in a branch it created if something didn’t work.&lt;/p&gt;

&lt;p&gt;Using a task like it was a chat and trying to fix more than one issue per task got confusing. Then I figured out I needed a different workflow:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 chat per branch, but can do multiple related things&lt;/li&gt;
  &lt;li&gt;1 chat can be as big as 1 feature&lt;/li&gt;
  &lt;li&gt;Code review each branch&lt;/li&gt;
  &lt;li&gt;If there are issues, go back to the chat and have Jules fix them&lt;/li&gt;
  &lt;li&gt;If there are build errors, tell Jules specifically to build the app again, test, and fix any errors.&lt;/li&gt;
  &lt;li&gt;Pull and test again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was used to my Claude Code process where everything happened in the branch I was on locally. The “Publish Branch” button in Jules seemed scary at first, but the second time I used it, it just pushed modifications to the existing branch. I was expecting merge conflicts or some other drama.&lt;/p&gt;

&lt;p&gt;Most things I asked Jules to do worked well, except for the chapter names issue. I also didn’t ask it to create unit tests, but it kept creating broken ones anyway. It each branch it created, I ran them once and if they failed, I deleted them. They mainly failed because of TypeScript type issues.&lt;/p&gt;

&lt;h2 id=&quot;the-future-of-lazy-coding&quot;&gt;The Future of Lazy Coding&lt;/h2&gt;

&lt;p&gt;Sixty tasks a day for free is generous. I’m planning to move everything I have to GitHub that isn’t already there and create issues for any feature ideas or bugs I encounter.&lt;/p&gt;

&lt;p&gt;I could envision a process where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A user puts in a support request&lt;/li&gt;
  &lt;li&gt;An issue gets created on the repo&lt;/li&gt;
  &lt;li&gt;I tell Jules to fix it&lt;/li&gt;
  &lt;li&gt;Jules fixes it, creates a custom build, and the user tests it (don’t know about this, but I can dream)&lt;/li&gt;
  &lt;li&gt;If the fix works, I merge it and create a new release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not sure exactly how this process would work in practice, but the potential is there.&lt;/p&gt;

&lt;h2 id=&quot;verdict-worth-the-price-of-free&quot;&gt;Verdict: Worth the Price of Free&lt;/h2&gt;

&lt;p&gt;Jules isn’t perfect. It ignored my initial instructions, created broken unit tests, and couldn’t fix the most complex issue I threw at it. But it’s fast, it’s free for now, and it actually solved most of the problems I needed solved.&lt;/p&gt;

&lt;p&gt;In two hours, I got a new feature and fixed three out of four issues that had been sitting in my backlog. The fourth issue would have taken me some research into ePub structure even doing it manually.&lt;/p&gt;

&lt;p&gt;I’m definitely planning to shift more of my “vibe coding” projects to Jules. At 60 free tasks per day, I can afford to let it work on the boring stuff while I focus on the interesting problems. In fact, to use 60 tasks a day, I might have to try running concurrent tasks and I can do 5 of those.&lt;/p&gt;

&lt;p&gt;And if it goes off on another tangent and builds something I didn’t ask for? Well, sometimes the best features are the ones you never knew you needed.&lt;/p&gt;
</description>
        <pubDate>Tue, 10 Jun 2025 08:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/using-jules-to-update-my-obsidian-plugin/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/using-jules-to-update-my-obsidian-plugin/</guid>
        
        <category>llm/coding</category>
        
        
        <category>vibe-coding</category>
        
        <category>programming</category>
        
        <category>javascript</category>
        
      </item>
    
      <item>
        <title>Creating an Obsidian Plugin with Claude AI</title>
        <description>&lt;p&gt;With this post, I took a break from the &lt;a href=&quot;https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/&quot;&gt;Electron project I am building with Claude Code&lt;/a&gt; to see how fast I could get a smaller project done. Since I am building a relatively simple Obsidian plugin, I went back to using Claude Desktop with the &lt;a href=&quot;https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking&quot;&gt;sequential thinking&lt;/a&gt; and &lt;a href=&quot;https://github.com/wonderwhy-er/DesktopCommanderMCP&quot;&gt;desktop commander&lt;/a&gt; MCP tools.&lt;/p&gt;

&lt;p&gt;And I learned a few more things about using AI to write code. Read on for the complete story&lt;/p&gt;

&lt;h2 id=&quot;the-story-behind-the-project&quot;&gt;The Story Behind the Project&lt;/h2&gt;

&lt;p&gt;I’m obsessed with having all my book highlights and notes in one place, and Obsidian seemed like a great place for them. First, I paid for a year’s Readwise subscription, which really didn’t cost too much and did the job well, but after the year, I realized that I really only used it to import my highlights from Kindle and Apple Books and there were free Obsidian plugins to import Kindle highlights.&lt;/p&gt;

&lt;p&gt;Then I discovered that Apple Books stored all these highlights and notes in an open though hard to find and hard to understand SQLite database. So I wrote a &lt;a href=&quot;https://www.stephanmiller.com/import-osx-book-notes-into-obsidian/&quot;&gt;Python script to import Apple Books annotations&lt;/a&gt;, using example from sources like this &lt;a href=&quot;https://github.com/davidfor/calibre-annotations/blob/master/readers/_iBooks.py&quot;&gt;Calibre plugin repo&lt;/a&gt; and used the Obsidian Python Scripter plugin to run it from Obsidian. I only had AI help when I ran into issues for the first script and wrote most of it myself.&lt;/p&gt;

&lt;h2 id=&quot;problems-with-the-original-python-script&quot;&gt;Problems with the Original Python Script&lt;/h2&gt;

&lt;p&gt;The first issue with the Python script I wrote is that the Python Scripter plugin is no longer available. This was not much of a problem though. I just hard-coded the file path of my vault’s book notes folder in the script and it still worked for me.&lt;/p&gt;

&lt;p&gt;Only it wasn’t really working the way I thought it was. There were some issues:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The annotations were not sorted by the location in the book.&lt;/li&gt;
  &lt;li&gt;The annotations weren’t by chapter (there is still an issue in the current plugin in that I have chapters now, but they are not human readable).&lt;/li&gt;
  &lt;li&gt;There were no configuration options. I just edited the script when I wanted to change something.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;phase-1-perfecting-the-python-script-with-claude&quot;&gt;Phase 1: Perfecting the Python Script with Claude&lt;/h2&gt;

&lt;p&gt;Before jumping right in to creating a plugin, I fixed the Python script’s sorting issue first. My reasoning was that if I got stuck in the plugin development process, I would at least have this script and it would work. And once it did, I could have Claude look at it for a working example. And it turns out this was a good move for these exact reasons.&lt;/p&gt;

&lt;p&gt;I also thought I could create this plugin with only a Claude Project and no MCP support, but I was wrong about not using MCP. It might have been possible, but MCP tools made things easier. Here was my first message:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I am using this Python file to import highlights and notes from the Mac iBooks app. I don’t think I am rendering them in order from the front of the book to the back in the markdown file. Let me know if I am and if not how to fix it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-sorting-challenge&quot;&gt;The Sorting Challenge&lt;/h3&gt;

&lt;p&gt;Claude initially gave me three sorting options, but none actually sorted by book location. To test the sort, I put highlights in a book with notes that told their order by location and by when I entered them. So I sent this message, along with a dump of the SQLite table that had the annotations:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The first method didn’t seem to work. The second two seemed to sort by date entered or modified instead of by location in the book. Here is the dump of that database table.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The table dump was key, because Claude analyzed the CFI strings that I’d been struggling with and created a sophisticated parser that could extract positional information from cryptic strings like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;epubcfi(/6/24[c3.xhtml]!/4/188/2/1,:0,:1)&lt;/code&gt;. And I was done with that until the end, when I had to revisit it multiple times in the plugin version.&lt;/p&gt;

&lt;h3 id=&quot;adding-chapter-detection&quot;&gt;Adding Chapter Detection&lt;/h3&gt;

&lt;p&gt;With sorting fixed, I pushed further:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Now that we have that fixed, is there a way to put chapter headings where they belong in the resulting markdown?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because I was using a small set of 5 books with highlights, this seemed to work well. Initially, chapters in some books came out like “c3.xhtml” and various other things. But it magically fixed that.&lt;/p&gt;

&lt;p&gt;But in the end, it was not magic. And just to let you know, I was using Claude 4, and Claude 4 will use workarounds and not tell you about it. When I tested the script on an account that had 60 books with highlights, none of the chapters in the results markdown were human readable.&lt;/p&gt;

&lt;p&gt;I looked into what Claude wrote and it was basically a hack. It was a series of ifs customized to that first set of 5 books, so only they came out right. These are still not human readable in the plugin. I figure I have to use the SQLite results in unison with the ePub file data to make them so, but that’s what new features are for.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/new-obsidian-annotations-md-template.png&quot; alt=&quot;new-obsidian-annotations-md-template&quot; srcset=&quot;            /assets/resized/480/new-obsidian-annotations-md-template.png 480w,            /assets/resized/800/new-obsidian-annotations-md-template.png 800w,            /assets/resized/1400/new-obsidian-annotations-md-template.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;enhanced-features&quot;&gt;Enhanced Features&lt;/h3&gt;

&lt;p&gt;I wanted to make sure I added everything I could to the Python script I could before moving onto the plugin and added:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Citation-ready format&lt;/li&gt;
  &lt;li&gt;More metadata extraction&lt;/li&gt;
  &lt;li&gt;Better error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the code for that &lt;a href=&quot;https://gist.github.com/eristoddle/5a8e7dd0597d09d00aa5de066788c303&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;phase-2-converting-to-an-obsidian-plugin&quot;&gt;Phase 2: Converting to an Obsidian Plugin&lt;/h2&gt;

&lt;p&gt;It took me about an hour to update the Python script. I figured I was on a roll. I had working code, and all Claude had to do was convert it to JavaScript and make it an Obsidian plugin. Famous last words. There is always something and many times it is something stupid.&lt;/p&gt;

&lt;p&gt;I had updated the Python script in a Claude chat inside of a Claude project, but had yet added any Project Resources. Now that I was going to work on the plugin, I uploaded these there:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The final Python script:&lt;/strong&gt; So it could reference this instead of reinventing the wheel.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The blog post I wrote about the original Python version:&lt;/strong&gt; I figured it would explain what it was doing and what I was trying to do.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The Obsidian sample plugin repository:&lt;/strong&gt; I am not sure I needed this. Maybe at the beginning, but I eventually realized (duh), that project resources become part of the context, which means your chats get cut off quicker, so I removed it. It never had problems with the plugin API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I sent this message:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have attached a Python script to import apple books annotations. I have also attached the Obsidian plugin sample for reference. I have also attached an article on how an older version of the Python script integrates with Obsidian. Help me create an Obsidian plugin that does the same thing, step by step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-development-process&quot;&gt;The Development Process&lt;/h3&gt;

&lt;p&gt;Claude recognized I had sequential thinking MCP and used it right away and came up with a plan.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-generate-obsidian-plugin.png&quot; alt=&quot;claude-generate-obsidian-plugin&quot; srcset=&quot;            /assets/resized/480/claude-generate-obsidian-plugin.png 480w,            /assets/resized/800/claude-generate-obsidian-plugin.png 800w,            /assets/resized/1400/claude-generate-obsidian-plugin.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But I forgot to tell Claude where the project was located, so after that, it spit out all the files in the chat. I quickly corrected that issue by telling it to create all the files in my project itself.&lt;/p&gt;

&lt;p&gt;The initial conversion went surprisingly smoothly:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Project structure creation&lt;/strong&gt; - all necessary TypeScript files&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Database access logic&lt;/strong&gt; - converting Python SQLite to JavaScript&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Markdown generation&lt;/strong&gt; - translating the formatting logic&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Settings interface&lt;/strong&gt; - creating a proper Obsidian settings panel, which I was even worried about at this point.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-annotation-import-settings.png&quot; alt=&quot;obsidian-annotation-import-settings&quot; srcset=&quot;            /assets/resized/480/obsidian-annotation-import-settings.png 480w,            /assets/resized/800/obsidian-annotation-import-settings.png 800w,            /assets/resized/1400/obsidian-annotation-import-settings.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;technical-challenges-and-solutions&quot;&gt;Technical Challenges and Solutions&lt;/h3&gt;

&lt;p&gt;Well, it was functional. But let’s just say I was not even halfway done yet. Also, and I am so tired of this, Claude corrected my name everywhere from “Stephan” to “Stephen”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-getting-my-name-wrong.png&quot; alt=&quot;claude-getting-my-name-wrong&quot; srcset=&quot;            /assets/resized/480/claude-getting-my-name-wrong.png 480w,            /assets/resized/800/claude-getting-my-name-wrong.png 800w,            /assets/resized/1400/claude-getting-my-name-wrong.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Just like Google always does.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/fuck-you-google.png&quot; alt=&quot;fuck-you-google&quot; srcset=&quot;            /assets/resized/480/fuck-you-google.png 480w,            /assets/resized/800/fuck-you-google.png 800w,            /assets/resized/1400/fuck-you-google.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;No, Google, my name is “Stephan.” But back on track. Here are the issues I ran into after I had a “working” Obsidian plugin.&lt;/p&gt;

&lt;h4 id=&quot;sqlite-library-issues&quot;&gt;SQLite Library Issues&lt;/h4&gt;

&lt;p&gt;A big hurdle was database access. Claude initially tried &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;better-sqlite3&lt;/code&gt; but ran into Electron compatibility issues. We switched to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sql.js&lt;/code&gt;, a pure JavaScript SQLite implementation, but then faced WebAssembly loading problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt; Configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sql.js&lt;/code&gt; with proper WASM file handling in the Obsidian environment.&lt;/p&gt;

&lt;h4 id=&quot;query-formatting-problems&quot;&gt;Query Formatting Problems&lt;/h4&gt;

&lt;p&gt;Even after fixing the SQLite library, we had issues with SQL query formatting when passed to the command line. Claude described the approach as “bulletproof” right before it broke, which became a running theme.&lt;/p&gt;

&lt;h4 id=&quot;chapter-parsing-regression&quot;&gt;Chapter Parsing Regression&lt;/h4&gt;

&lt;p&gt;One book showed chapters as “Ahr5106 us trade bbp text 2” instead of readable names. This required Claude to revisit the CFI parsing logic and ensure consistency with the Python version. This is still a challenge and I am going to do research before I try to fix this.&lt;/p&gt;

&lt;h3 id=&quot;the-epub-metadata-challenge&quot;&gt;The EPUB Metadata Challenge&lt;/h3&gt;

&lt;p&gt;It was now two hours since I started revising the original Python script. The Python script used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ebooklib&lt;/code&gt; to extract book covers and enhanced metadata. Finding a JavaScript equivalent proved challenging. And I was a little gun shy by now, so I asked:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is there anything in JavaScript that will do the same thing the python script did with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ebooklib&lt;/code&gt;, like get the cover image and other metadata? If you know of something, do not commit code until I say it is working. It works now and I don’t want to commit broken code.&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;Claude suggested three options, and I chose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;epub2&lt;/code&gt; for its popularity and feature set. However, it took several rounds of debugging across multiple chat sessions to get ePub parsing working correctly.&lt;/p&gt;

&lt;h3 id=&quot;real-world-testing-challenges&quot;&gt;Real-World Testing Challenges&lt;/h3&gt;

&lt;p&gt;Testing on my actual Apple Books account (with 4,711 book) shook some new bugs loose:&lt;/p&gt;

&lt;h4 id=&quot;database-schema-variations&quot;&gt;Database Schema Variations&lt;/h4&gt;

&lt;p&gt;The plugin tried to query columns that didn’t exist on my primary account, causing SQLite errors. The Python script handled this gracefully, but the plugin needed updates.&lt;/p&gt;

&lt;h4 id=&quot;memory-and-buffer-limits&quot;&gt;Memory and Buffer Limits&lt;/h4&gt;

&lt;p&gt;With thousands of books, we hit “maxBuffer length exceeded” errors. Claude implemented chunked processing to handle large datasets.&lt;/p&gt;

&lt;h4 id=&quot;annotation-grouping-issues&quot;&gt;Annotation Grouping Issues&lt;/h4&gt;

&lt;p&gt;The plugin kept breaking up annotations that should have been together and duplicating others. This took six rounds of debugging, with Claude repeatedly missing the fact that the Python version worked perfectly.&lt;/p&gt;

&lt;p&gt;I finally resorted to extended thinking:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Think as hard as you can about the fact that the Python script is working, and the plugin is not and the only thing happening is SQLite queries and handling the results, so this should be fucking simple.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;the-development-timeline&quot;&gt;The Development Timeline&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Hour 1-2:&lt;/strong&gt; Python script update, initial plugin creation, and basic functionality&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hour 3:&lt;/strong&gt; Fighting with ePub metadata extraction&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hour 4-6:&lt;/strong&gt; Real-world testing, debugging on large dataset, and configuration tweaking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total development time: About 6 hours across multiple sessions, with Claude handling the bulk of the coding.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-create-obsidian-plugin-results.png&quot; alt=&quot;claude-create-obsidian-plugin-results&quot; srcset=&quot;            /assets/resized/480/claude-create-obsidian-plugin-results.png 480w,            /assets/resized/800/claude-create-obsidian-plugin-results.png 800w,            /assets/resized/1400/claude-create-obsidian-plugin-results.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-i-learned-this-time&quot;&gt;What I Learned This Time&lt;/h2&gt;

&lt;h3 id=&quot;the-good&quot;&gt;The Good&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Rapid prototyping:&lt;/strong&gt; Claude excels at converting working logic between languages most times, but not understanding that it could use the same SQLite queries in both JavaScript and Python was a pain.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Comprehensive solutions:&lt;/strong&gt; Often suggests improvements you haven’t considered&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pattern recognition:&lt;/strong&gt; Great at handling complex parsing tasks like CFI strings&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; Automatically generates thorough README files and comments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-challenging&quot;&gt;The Challenging&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Context switching:&lt;/strong&gt; Moving between chat sessions sometimes loses important context&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Overconfidence:&lt;/strong&gt; Claims solutions are “bulletproof” right before they break&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Debugging persistence:&lt;/strong&gt; Sometimes fixates on wrong solutions instead of reverting to working code&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Name corrections:&lt;/strong&gt; Consistently “corrected” my name spelling throughout the project&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tips-for-successful-claude-desktop-coding&quot;&gt;Tips for Successful Claude Desktop Coding&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Use projects&lt;/strong&gt; for better context persistence&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Be specific&lt;/strong&gt; about what’s working vs. broken&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maintain working versions&lt;/strong&gt; before attempting fixes&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test incrementally&lt;/strong&gt; rather than making multiple changes at once&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Provide concrete examples&lt;/strong&gt; when debugging&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-results&quot;&gt;The Results&lt;/h2&gt;

&lt;p&gt;I am happy with the results. There is still some more work to do:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Not sure what is happening with the covers. The Python version had much more luck finding the covers, but then it used a more extensive library to do it.&lt;/li&gt;
  &lt;li&gt;The chapter names are still in gibberish in general. I think is because it is just using the value in the CFI location string. I am guessing I have to look this up somehow in the ePub file.&lt;/li&gt;
  &lt;li&gt;There seems to be a configuration value for adding the annotation date to each entry, but I haven’t seen one. Not sure what is going on there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for now I can use it and when I fix it, just have it overwrite all the old files. Then, once I am happy with it, store a hash of the file in properties or something like that and have overwriting work more intelligently.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/obsidian-book-note-example.png&quot; alt=&quot;obsidian-book-note-example&quot; srcset=&quot;            /assets/resized/480/obsidian-book-note-example.png 480w,            /assets/resized/800/obsidian-book-note-example.png 800w,            /assets/resized/1400/obsidian-book-note-example.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-install-the-apple-books-highlight-and-note-importer&quot;&gt;How to Install the Apple Books Highlight and Note Importer&lt;/h2&gt;

&lt;p&gt;I do plan on releasing this as a community plugin. I just want to use it a while to see if there are any more bugs I want to fix or features I want to add.&lt;/p&gt;

&lt;p&gt;But for now the simplest way to install and test this plugin is with &lt;a href=&quot;https://github.com/TfTHacker/obsidian42-brat&quot;&gt;BRAT (Beta Reviewer’s Auto-update Tool)&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Install BRAT&lt;/strong&gt; from the Community Plugins store in Obsidian&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Open BRAT settings&lt;/strong&gt; (Settings → BRAT)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Click “Add Beta plugin”&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enter this repository URL&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://github.com/eristoddle/obsidian-apple-books-import&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Click “Add Plugin”&lt;/strong&gt; - Choose the latest version and BRAT will automatically install and enable it&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auto-updates&lt;/strong&gt;: BRAT will automatically update the plugin when new versions are released if you choose the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; version from the dropdown.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;

&lt;p&gt;I took a break from a more complex project for a day to see if I could finish a smaller one. And even though there is still more work to do here, I think it was a success and an excellent test. I will continue to explore new ways of making the “vibe coding” process go smoother. In the couple of days that it took me to write this, I found even more tools to help.&lt;/p&gt;

&lt;p&gt;Because I have no end of software ideas I have been collecting, but never got to, because I never had the time. So I will continue to work on my main vibe-coding project while taking a break to test new ways of doing this on smaller projects. My next post should be about the next set of things I learned &lt;a href=&quot;https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/&quot;&gt;developing an Electron app with Claude Code&lt;/a&gt;. I already have notes on it, but wanted to try this first.&lt;/p&gt;

</description>
        <pubDate>Mon, 02 Jun 2025 08:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/creating-an-obsidian-plugin-with-claude/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/creating-an-obsidian-plugin-with-claude/</guid>
        
        
        <category>vibe-coding</category>
        
        <category>obsidian</category>
        
        <category>javascript</category>
        
        <category>python</category>
        
      </item>
    
      <item>
        <title>Building an Electron App from Scratch with Claude Code</title>
        <description>&lt;p&gt;I have been curious about “vibe coding” ever since I heard about the process, even though I hate the name. I started by &lt;a href=&quot;https://www.stephanmiller.com/using-mcps-with-claude-desktop/&quot;&gt;using Claude Desktop with MCP tools&lt;/a&gt; to build a code project from scratch and it felt like magic. That was until I discovered Claude Code and my workflow got so much easier and less chaotic right away.&lt;/p&gt;

&lt;p&gt;This post documents my adventure so far, using Claude Code to build an Electron writing app. I made a few mistakes and am still making them, but corrected them and think I am on a better path now. I doubt it’s the right path, but I’ll keep tweaking it until it works for me.&lt;/p&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#vibe-coding-with-claude-desktop-and-mcp&quot; id=&quot;markdown-toc-vibe-coding-with-claude-desktop-and-mcp&quot;&gt;Vibe Coding with Claude Desktop and MCP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#features-of-claude-code-that-made-me-switch&quot; id=&quot;markdown-toc-features-of-claude-code-that-made-me-switch&quot;&gt;Features of Claude Code that Made Me Switch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#installing-and-using-claude-code&quot; id=&quot;markdown-toc-installing-and-using-claude-code&quot;&gt;Installing and Using Claude Code&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#requirements&quot; id=&quot;markdown-toc-requirements&quot;&gt;Requirements&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#installing-and-initializing&quot; id=&quot;markdown-toc-installing-and-initializing&quot;&gt;Installing and Initializing&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#building-a-project-from-scratch-with-claude-code&quot; id=&quot;markdown-toc-building-a-project-from-scratch-with-claude-code&quot;&gt;Building a Project from Scratch with Claude Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-pain-of-refactoring-with-claude-code&quot; id=&quot;markdown-toc-the-pain-of-refactoring-with-claude-code&quot;&gt;The Pain of Refactoring with Claude Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#my-current-claudemd-file-and-coding-process&quot; id=&quot;markdown-toc-my-current-claudemd-file-and-coding-process&quot;&gt;My Current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file and Coding Process&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#how-to-use-this-file&quot; id=&quot;markdown-toc-how-to-use-this-file&quot;&gt;How to Use This File&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#project-overview&quot; id=&quot;markdown-toc-project-overview&quot;&gt;Project Overview&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#key-requirements&quot; id=&quot;markdown-toc-key-requirements&quot;&gt;Key Requirements&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#important-references&quot; id=&quot;markdown-toc-important-references&quot;&gt;Important References&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#code-style-guidelines&quot; id=&quot;markdown-toc-code-style-guidelines&quot;&gt;Code Style Guidelines&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#project-sdlc&quot; id=&quot;markdown-toc-project-sdlc&quot;&gt;Project SDLC&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#buildlinttest-commands&quot; id=&quot;markdown-toc-buildlinttest-commands&quot;&gt;Build/Lint/Test Commands&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#development-status&quot; id=&quot;markdown-toc-development-status&quot;&gt;Development Status&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#next-development-steps&quot; id=&quot;markdown-toc-next-development-steps&quot;&gt;Next Development Steps&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#qa-checklist&quot; id=&quot;markdown-toc-qa-checklist&quot;&gt;QA Checklist&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lessons-i-learned-using-claude-code&quot; id=&quot;markdown-toc-lessons-i-learned-using-claude-code&quot;&gt;Lessons I Learned Using Claude Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#features-of-claude-code-i-havent-tried-yet&quot; id=&quot;markdown-toc-features-of-claude-code-i-havent-tried-yet&quot;&gt;Features of Claude Code I Haven’t Tried Yet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#claude-code-tips-from-others-i-plan-on-trying&quot; id=&quot;markdown-toc-claude-code-tips-from-others-i-plan-on-trying&quot;&gt;Claude Code Tips From Others I Plan on Trying&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-claude-code-built-screenshots-of-my-app&quot; id=&quot;markdown-toc-what-claude-code-built-screenshots-of-my-app&quot;&gt;What Claude Code Built: Screenshots of My app&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#dashboard&quot; id=&quot;markdown-toc-dashboard&quot;&gt;Dashboard&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#settings&quot; id=&quot;markdown-toc-settings&quot;&gt;Settings&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#writing-interface&quot; id=&quot;markdown-toc-writing-interface&quot;&gt;Writing Interface&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#character-relationship-graph&quot; id=&quot;markdown-toc-character-relationship-graph&quot;&gt;Character Relationship Graph&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#my-journey-with-claude-code-continues&quot; id=&quot;markdown-toc-my-journey-with-claude-code-continues&quot;&gt;My Journey with Claude Code Continues&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#what-ive-learned&quot; id=&quot;markdown-toc-what-ive-learned&quot;&gt;What I’ve Learned&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#whats-next&quot; id=&quot;markdown-toc-whats-next&quot;&gt;What’s Next&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;vibe-coding-with-claude-desktop-and-mcp&quot;&gt;Vibe Coding with Claude Desktop and MCP&lt;/h2&gt;

&lt;p&gt;When I was &lt;a href=&quot;https://www.stephanmiller.com/using-mcps-with-claude-desktop/&quot;&gt;using Claude Desktop to write code&lt;/a&gt;, I always felt like I was using workarounds to plan and organize. I started out with a standalone chat, but eventually got cut off and had to start a new chat to continue working on the app. So I switched to using a Claude Project.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.stephanmiller.com/vibe-coding-with-claude-desktop-part-2/&quot;&gt;Working on the app in a Claude Project&lt;/a&gt; made things a little easier by allowing me to upload 50 reference documents, but that was a mess, because the project was always changing and I had to delete and re-upload changes to documentation all the time. I could have also tried connected to the project’s Github repo or simply putting the documentation in a folder in the local project.&lt;/p&gt;

&lt;p&gt;But around that time, I ran into &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;Claude Code&lt;/a&gt;, read up on it, and figured it would work better for what I needed, so I started a new project with it. I might go back to the first project with a better plan, because I was making progress.&lt;/p&gt;

&lt;h2 id=&quot;features-of-claude-code-that-made-me-switch&quot;&gt;Features of Claude Code that Made Me Switch&lt;/h2&gt;

&lt;p&gt;I am using to a &lt;strong&gt;command-line interface&lt;/strong&gt;. I use it them all the time. It feels like I am doing work. In chat, I feel like I am getting nagged by a sales bot on an e-commerce site or arguing with customer service. I get it makes no sense, but the command-line interface was a big feature for me.&lt;/p&gt;

&lt;p&gt;Another significant feature is the &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file&lt;/strong&gt;, which Claude Code which holds your project memory. You can add things to it, like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Build/Lint/Test Commands&lt;/li&gt;
  &lt;li&gt;Code Style Guidelines&lt;/li&gt;
  &lt;li&gt;The SDLC process you want to use&lt;/li&gt;
  &lt;li&gt;Reference other documentation on the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means I can keep its memory in the project, instead of project files. When I first started working with Claude, I started setting up MCP connections like I did with Desktop. Gut then realized I was wasting my time and adding overhead, because &lt;strong&gt;the functionality I was getting by adding MCPs to Claude Desktop was already built into Code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I could see adding a database connection MCP in the future if I work on a project that has one, but right now I am not using any with Claude Code. Claude Code has a lot more features than the three I listed here and I will get to them later, but these were all the reasons I needed to make the switch.&lt;/p&gt;

&lt;h2 id=&quot;installing-and-using-claude-code&quot;&gt;Installing and Using Claude Code&lt;/h2&gt;

&lt;p&gt;I have to admit that I basically ran the code to install Claude Code, ran the init command in a new project folder, and starting playing with it. Writing this, I figured I should dig deeper into its documentation to make sure I missed nothing. And I missed a few things that might have made this entire process smoother.&lt;/p&gt;

&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Operating Systems&lt;/strong&gt;: macOS 10.15+, Ubuntu 20.04+/Debian 10+, or Windows via WSL&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hardware&lt;/strong&gt;: 4GB RAM minimum&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Software&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;Node.js 18+&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;git&lt;/a&gt; 2.23+ (optional)&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://cli.github.com/&quot;&gt;GitHub&lt;/a&gt; or &lt;a href=&quot;https://gitlab.com/gitlab-org/cli&quot;&gt;GitLab&lt;/a&gt; CLI for PR workflows (optional)&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/BurntSushi/ripgrep?tab=readme-ov-file#installation&quot;&gt;ripgrep&lt;/a&gt; (rg) for enhanced file search (optional)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing I missed there was installing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ripgrep&lt;/code&gt; for advanced search. Maybe that will make things quicker? I did not install it until I started writing this, so maybe that is why things seem to take longer than they needed.&lt;/p&gt;

&lt;h3 id=&quot;installing-and-initializing&quot;&gt;Installing and Initializing&lt;/h3&gt;

&lt;p&gt;To get started, run this command to install Claude Code globally:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then navigate to your project’s folder and run:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After Claude Code started, I ran the following command to generate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;building-a-project-from-scratch-with-claude-code&quot;&gt;Building a Project from Scratch with Claude Code&lt;/h2&gt;

&lt;p&gt;I wasn’t sure I could do this because all the examples online were working with existing codebases and I wanted to start one from scratch. So I asked Claude, and it said I could.&lt;/p&gt;

&lt;p&gt;And I decided I would build an Electron app. A writing app. Yeah, I know. Like we need another one of those. But it was something I had always wanted to do and have been collecting a list of features over the years, which only made the odds I would ever get to it worse.&lt;/p&gt;

&lt;p&gt;And, of course, you can’t make a writing app these days without including AI, so I am using AI to write an AI writing app. And with all the middlemen in the AI world, I figured I would make it so you just enter your LLM API keys and get started. The app would be completely self-contained in an Electron desktop app. For now, at least.&lt;/p&gt;

&lt;p&gt;But if I were going to start over, I would start with more of a plan. I basically pointed Claude Code to the features of other similar apps and told it I wanted to build an Electron app with similar features.&lt;/p&gt;

&lt;p&gt;As I chatted with Claude Code and worked on the app for the first couple of hours, I just let it update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; with the project details.&lt;/p&gt;

&lt;h2 id=&quot;the-pain-of-refactoring-with-claude-code&quot;&gt;The Pain of Refactoring with Claude Code&lt;/h2&gt;

&lt;p&gt;I was learning my mistakes as I went. The one I learned quickly was to not only plan features, but plan the architecture that the features will work in. Think ahead. By the time I had an app where some features were working enough to be useful, I decided I should have used a plugin architecture.&lt;/p&gt;

&lt;p&gt;I realized this architecture would work better for what I was doing when I was adding a context menu for the editor. When you right click in an editor, there are many things you can have it do, but when you add a new action, you are basically just adding another item to that menu that runs a new function when it is clicked.&lt;/p&gt;

&lt;p&gt;Now I also think this type of architecture could be useful when using Claude Code to create an app like this. Have it build the core system and add a well-defined interface for plugins to interact with the core. Then build all the features as plugins. I think that this would keep the context lighter because Claude could use the plugin API docs to build the features instead of searching and parsing everything all the time.&lt;/p&gt;

&lt;p&gt;But moving toward an architecture like that later in the game is hard. My first attempt to globally change things in the project was successful, but it took quite a while. And I still have a way to go. So, currently I am looking through the code base to see what all has to change for the complete refactor to happen. That way, when I tell it to refactor, I can give it some guidance and know what to expect when it does it right.&lt;/p&gt;

&lt;h2 id=&quot;my-current-claudemd-file-and-coding-process&quot;&gt;My Current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file and Coding Process&lt;/h2&gt;

&lt;p&gt;For the first few hours, I just told Claude Code to build new features and keep track of where it was at in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file. But after a while, I knew I had to rein it in. Here are the sections I have in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file currently, which has changed often and will continue to:&lt;/p&gt;

&lt;h3 id=&quot;how-to-use-this-file&quot;&gt;How to Use This File&lt;/h3&gt;

&lt;p&gt;When I decided to refactor the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt;, I had some ideas, but also asked Claude Code what would work. It suggested this section. Here’s what it has in it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;This file contains essential project information&lt;/li&gt;
    &lt;li&gt;Detailed documentation can be found in referenced files&lt;/li&gt;
    &lt;li&gt;Update both this file and referenced files when making changes&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;project-overview&quot;&gt;Project Overview&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;EmberText is an AI-powered desktop writing application built with Electron, React, and TypeScript. It combines features from TypingMind, NovelCrafter, SudoWrite, and Scrivener.&lt;/p&gt;

  &lt;h3 id=&quot;key-requirements&quot;&gt;Key Requirements&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;Projects must be stored as markdown files in a folder structure (similar to Obsidian)&lt;/li&gt;
    &lt;li&gt;Each chapter should be saved as an individual markdown file&lt;/li&gt;
    &lt;li&gt;Project metadata and structure should be stored in a JSON file within the project directory&lt;/li&gt;
    &lt;li&gt;The app should be able to open and edit these files directly, enabling compatibility with other markdown editors&lt;/li&gt;
    &lt;li&gt;Help a writer complete a project with AI or other tools from idea to ebook publishing&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;important-references&quot;&gt;Important References&lt;/h3&gt;

&lt;p&gt;I had everything in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; file to begin with, but it quickly got huge. So I thought about how to trim it down. I also asked Claude Code what should be in the file versus what it could access on an as-needed basis. There is the concept of &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/memory#claude-md-imports&quot;&gt;imports&lt;/a&gt;, but I didn’t want to use that because I would essentially do the same thing, but putting this massive amount of information in multiple files that still got read every time I ran Claude Code.&lt;/p&gt;

&lt;p&gt;So I created a project knowledge base folder and put everything that it wouldn’t need every time there and add an “important references” section in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt;. Here’s what I have in this section:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;Code Project Structure: knowledge_base/project/structure.md&lt;/li&gt;
    &lt;li&gt;Writing Project Structure: knowledge_base/project/directory-structure.md&lt;/li&gt;
    &lt;li&gt;Key Features: knowledge_base/project/key-features.md&lt;/li&gt;
    &lt;li&gt;Potential Feature Ideas: knowledge_base/feature-ideas&lt;/li&gt;
    &lt;li&gt;Features Ready for Development or Already Developed: knowledge_base/features&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;code-style-guidelines&quot;&gt;Code Style Guidelines&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Formatting&lt;/strong&gt;: Use 2-space indentation, single quotes, no semicolons&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Imports&lt;/strong&gt;: Group imports (node builtins, external, internal)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Types&lt;/strong&gt;: Use TypeScript with explicit return types on functions&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Naming&lt;/strong&gt;: camelCase for variables/functions, PascalCase for classes/components&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Components&lt;/strong&gt;: One component per file, match filename to component name&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;: Use try/catch blocks with specific error types&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Functions&lt;/strong&gt;: Prefer pure functions, avoid side effects&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: Write unit tests for all new functionality&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;project-sdlc&quot;&gt;Project SDLC&lt;/h3&gt;

&lt;p&gt;As I was working on the project, I decided to “formalize” the development process, so that Claude Code knew what steps I wanted it to take. Here is what I have in this section:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;New features will be picked from “Key Features” in knowledge_base/project/key-features.md when “Next development steps” is empty. Once those are done, they will be sourced from knowledge_base/feature-ideas.&lt;/li&gt;
    &lt;li&gt;I will ask you to think about the features and you will create notes about the feature in a markdown file named after it in knowledge_base/features. This file will be used as reference when we update a feature or fix bugs. The file will be kept updated with the feature changes.&lt;/li&gt;
    &lt;li&gt;You will then reference the file next to the feature in “Next development steps” like this: “Add workshop chat: knowledge_base/features/AddWorkShopChat.md”&lt;/li&gt;
    &lt;li&gt;I will ask you to do the next task in “Next development steps”. You will work on it and tell me when it is done, asking any necessary questions along the way.&lt;/li&gt;
    &lt;li&gt;When that is done, you will move the feature from “Next development steps” to “Current progress” and make sure to move the reference to the note in knowledge_base/features to it.&lt;/li&gt;
    &lt;li&gt;You will also update “Key Features” in knowledge_base/project/key-features.md if anything changed there.&lt;/li&gt;
    &lt;li&gt;Update the project structure in knowledge_base/project/structure.md and writing project structure in knowledge_base/project/directory-structure.md when necessary.&lt;/li&gt;
    &lt;li&gt;You will add testing the feature to “QA Checklist”&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;buildlinttest-commands&quot;&gt;Build/Lint/Test Commands&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;Install: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Start: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt; (builds main and renderer processes and starts Electron)&lt;/li&gt;
    &lt;li&gt;Dev: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run dev&lt;/code&gt; (development mode with live reload)&lt;/li&gt;
    &lt;li&gt;Build Main: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run build:main&lt;/code&gt; (TypeScript compilation for main process)&lt;/li&gt;
    &lt;li&gt;Build Renderer: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run build:renderer&lt;/code&gt; (Webpack bundling for renderer)&lt;/li&gt;
    &lt;li&gt;Test: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm test&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Lint: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run lint&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Format: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run format&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;development-status&quot;&gt;Development Status&lt;/h3&gt;

&lt;p&gt;This is a list of features it has already implemented. Just a simple description of the feature and the path to the feature plan file. More details on the feature plan file below.&lt;/p&gt;

&lt;h3 id=&quot;next-development-steps&quot;&gt;Next Development Steps&lt;/h3&gt;

&lt;p&gt;This is a list of features I told it to put here. I usually have 3-4 features in this list. When I tell it to add a feature, I have it “think hard” about how to implement it, create a file in my project knowledge base folder that details how the feature will be implemented, and add it to this list like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Implement scene scaffolding system: knowledge_base/features/SceneScaffolding.md&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives me a chance to look it over before it implements it. I am also hoping it follows my instructions and only references those files when dealing with that specific feature to keep unnecessary information out of the context. The files it creates are pretty extensive and have the following headings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Overview&lt;/li&gt;
  &lt;li&gt;Key Requirements&lt;/li&gt;
  &lt;li&gt;User Interface Components&lt;/li&gt;
  &lt;li&gt;Data Structures&lt;/li&gt;
  &lt;li&gt;Components to Create&lt;/li&gt;
  &lt;li&gt;AI Integration&lt;/li&gt;
  &lt;li&gt;User Experience Flow&lt;/li&gt;
  &lt;li&gt;Integration with Existing Features&lt;/li&gt;
  &lt;li&gt;Implementation Plan&lt;/li&gt;
  &lt;li&gt;Dependency Notes&lt;/li&gt;
  &lt;li&gt;Testing Requirements&lt;/li&gt;
  &lt;li&gt;Future Enhancements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;qa-checklist&quot;&gt;QA Checklist&lt;/h3&gt;

&lt;p&gt;Claude Code will add items to this list when it has finished a feature. It’s actually a reminder for me to test new features, so it probably doesn’t need to be in the file. For now, though, it keeps me from forgetting to test things and it doesn’t take that much space.&lt;/p&gt;

&lt;h2 id=&quot;lessons-i-learned-using-claude-code&quot;&gt;Lessons I Learned Using Claude Code&lt;/h2&gt;

&lt;p&gt;I learned most of these lessons after making many mistakes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Plan well:&lt;/strong&gt; This is a lesson I keep learning over and over. But I still jumped right into this project without much of a plan. All I had was Electron and a list of features. I should have thought more about architecture at the beginning instead of refactoring a few hours in.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Stop and look at the project holistically often:&lt;/strong&gt; Especially if you don’t plan well. I knew I wanted AI to help write a book at every step, from idea to ebook, but I sort of just let it do what it wanted. And by the time I realized the AI functionality could be a modal used in multiple places, it put AI forms in three or four different places.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tell Claude Code to “think harder” when you are planning feature:&lt;/strong&gt; This is the third lesson that involves “planning.” Notice a pattern. When you tell Claude to “think”, it triggers &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/tutorials#use-extended-thinking&quot;&gt;Claude’s extending thinking&lt;/a&gt; and when you tell it to “think harder”, “think a lot”, or “think more”, it triggers deeper thinking.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do your own git commits:&lt;/strong&gt; I didn’t think that Claude Code would commit code without explicitly telling it to do so, but now and then it would. And each time, it was when I wanted to be done for the night and, of course, the code was broken. So I spent more time having it fix the code, so that I could commit working code. I could have reverted, but it had just added a new feature, which I knew was at least partially working.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;QA and review the UI often:&lt;/strong&gt; Claude will forget things. It will think hard about a feature, create a plan, work on it, tell you it is done, and forget 20% of the features. It was really hard for me to stop adding shiny new things to the app to do mundane testing, but I eventually had to backtrack through a lot of things and fix bugs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;features-of-claude-code-i-havent-tried-yet&quot;&gt;Features of Claude Code I Haven’t Tried Yet&lt;/h2&gt;

&lt;p&gt;I jumped right into this project without reading 95% of &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;the docs&lt;/a&gt;. But I figured I should at least peruse them before I wrote this. Here are some things I ran into, not covered by this article, you might want to check out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Free Claude Code with Claude Max:&lt;/strong&gt; I may or may not try this in the future. If you pay for the $100/month Claude Max subscription, Claude Code comes along with it. In my estimation, when I am working with Claude Code, it costs me around $5 an hour and with my limited time, I have only used about $50/month so far with the API. Maybe with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ripgrep&lt;/code&gt; installed, the process will go faster and cost me more per hour.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/tutorials#use-claude-code-as-an-mcp-server&quot;&gt;Use Claude Code as an MCP server&lt;/a&gt;:&lt;/strong&gt; Thought this was cool, but then wasn’t sure what I would use it for.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/tutorials#create-custom-slash-commands&quot;&gt;Create custom slash commands&lt;/a&gt;:&lt;/strong&gt; You can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/commands&lt;/code&gt; folder in your project to hold commands you use over and over. You can also design these commands to accept arguments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;claude-code-tips-from-others-i-plan-on-trying&quot;&gt;Claude Code Tips From Others I Plan on Trying&lt;/h2&gt;

&lt;p&gt;I work on projects like this and write articles about them in the evenings when I don’t have freelance writing work and in the last two weeks I haven’t been able to touch this project. But I still looked for tips whenever I could and took notes on them. Here are some things I plan on trying in the future:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Manage compacting more effectively:&lt;/strong&gt; I noticed that Claude Code mentioning something about compacting context and providing a percentage. Now, with further research, I’ve learned that Claude Code will compress the conversation history to stay within the context window’s limits. I found this &lt;a href=&quot;https://www.reddit.com/r/ClaudeAI/comments/1ko5pxk/claude_code_is_a_beast_tips_from_a_week_of/&quot;&gt;post on Reddit&lt;/a&gt; about always doing compacting manually. For example, before you are about to start on a new feature. Because, I guess, auto compacting can really screw you if it happens at the wrong time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Leverage other LLMs and &lt;a href=&quot;https://repomix.com/&quot;&gt;repomix&lt;/a&gt; or &lt;a href=&quot;https://github.com/mufeedvh/code2prompt&quot;&gt;code2prompt&lt;/a&gt;:&lt;/strong&gt; The refactoring process was a pain. Fixing bugs was a pain. &lt;a href=&quot;https://www.reddit.com/r/ClaudeAI/comments/1kkatqk/comment/mrtsfn2/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button&quot;&gt;Redditor squareboxrox mentioned&lt;/a&gt; using these tools to upload your whole project to another LLM when you run into the inevitable bug fix rabbit hole, instead of saying “still not fixed” over and over. Not every bug is like this. Maybe 20%. But I figured I’d give this method a try&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-claude-code-built-screenshots-of-my-app&quot;&gt;What Claude Code Built: Screenshots of My app&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; These screenshots showcase the functional aspects of EmberText rather than polished design. As this project focused on testing Claude Code’s capabilities for rapidly building functional features, I prioritized getting core functionality working over UI refinement. Consider these a “developer preview” of what’s possible with AI-assisted coding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;dashboard&quot;&gt;Dashboard&lt;/h3&gt;

&lt;p&gt;The home screen showing multiple writing projects with their structure and metadata. Claude Code implemented the card-based layout and project metadata tracking.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/embertext-workbench.png&quot; alt=&quot;embertext-workbench&quot; srcset=&quot;            /assets/resized/480/embertext-workbench.png 480w,            /assets/resized/800/embertext-workbench.png 800w,            /assets/resized/1400/embertext-workbench.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;settings&quot;&gt;Settings&lt;/h3&gt;

&lt;p&gt;Settings panel for connecting to various AI models. This shows how Claude Code implemented form validation and API connection management.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/embertext-settings.png&quot; alt=&quot;embertext-settings&quot; srcset=&quot;            /assets/resized/480/embertext-settings.png 480w,            /assets/resized/800/embertext-settings.png 800w,            /assets/resized/1400/embertext-settings.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;writing-interface&quot;&gt;Writing Interface&lt;/h3&gt;

&lt;p&gt;The main editor with project structure sidebar and formatting tools. Note the line-numbered editor and structure navigation that Claude Code implemented based on my requirements.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/embertext-project.png&quot; alt=&quot;embertext-project&quot; srcset=&quot;            /assets/resized/480/embertext-project.png 480w,            /assets/resized/800/embertext-project.png 800w,            /assets/resized/1400/embertext-project.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;character-relationship-graph&quot;&gt;Character Relationship Graph&lt;/h3&gt;

&lt;p&gt;An interactive visualization showing connections between characters. This feature shows Claude Code’s ability to integrate complex visualizations using React.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/embertext-relationships.png&quot; alt=&quot;embertext-relationships&quot; srcset=&quot;            /assets/resized/480/embertext-relationships.png 480w,            /assets/resized/800/embertext-relationships.png 800w,            /assets/resized/1400/embertext-relationships.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m impressed by how quickly Claude Code could assemble a functional writing application with complex features like relationship graphs and structured document editing. While the UI would benefit from refinement, the core functionality is there and working as intended after just 16 hours of development time.&lt;/p&gt;

&lt;h2 id=&quot;my-journey-with-claude-code-continues&quot;&gt;My Journey with Claude Code Continues&lt;/h2&gt;

&lt;p&gt;After spending about 16 hours and $80 in API costs, I’ve built a writing application with some functionality. My app currently:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Connects to Claude and Open API&lt;/li&gt;
  &lt;li&gt;Generates text and dialogue&lt;/li&gt;
  &lt;li&gt;Creates outline and plot structures&lt;/li&gt;
  &lt;li&gt;Has a plot timeline&lt;/li&gt;
  &lt;li&gt;Has a character relationship graph&lt;/li&gt;
  &lt;li&gt;Adds location, character, and item context to API calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The refactoring process I described earlier was painful enough that I have temporarily paused development to reorganize my project memory files and carefully plan upcoming features. It has also reinforced the biggest lesson I learned: &lt;strong&gt;planning saves significant time and frustration&lt;/strong&gt; with AI-assisted development.&lt;/p&gt;

&lt;h3 id=&quot;what-ive-learned&quot;&gt;What I’ve Learned&lt;/h3&gt;

&lt;p&gt;The most valuable lesson from this experiment is that Claude Code does well when given clear direction and architecture upfront. While it can adapt on the fly, major architectural shifts are still painful, much like traditional development, just faster.&lt;/p&gt;

&lt;p&gt;My approach now combines the benefits of AI coding with disciplined software design: I plan thoroughly, create detailed feature specifications, and only then engage Claude Code to implement them.&lt;/p&gt;

&lt;h3 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Complete my writing app&lt;/strong&gt;: Once I’ve implemented the remaining AI helpers, I’ll test the application with a real book project. Since I built this tool primarily for myself, this test will be the accurate measure of success.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Explore smaller projects&lt;/strong&gt;: The development speed has inspired me to tackle several smaller ideas in parallel. With proper planning, I could potentially complete 2-3 modest apps in the same time it would typically take to build one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re interested in trying Claude Code yourself, I recommend starting with something small and well-defined. The initial time investment in proper planning and documentation pays dividends in development speed and reduces frustrating rework.&lt;/p&gt;

&lt;p&gt;I’ll be documenting more of this journey as I continue exploring AI-assisted development.&lt;/p&gt;
</description>
        <pubDate>Tue, 20 May 2025 07:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/electron-project-from-scratch-with-claude-code/</guid>
        
        <category>llm/coding</category>
        
        
        <category>vibe-coding</category>
        
        <category>programming</category>
        
        <category>javascript</category>
        
      </item>
    
      <item>
        <title>Vibe Coding With Claude Desktop and MCP Part 2 - Switching to Scrapy</title>
        <description>&lt;p&gt;In my first &lt;a href=&quot;https://www.stephanmiller.com/using-mcps-with-claude-desktop/&quot;&gt;blog post on vibe coding with Claude Desktop and MCP tools&lt;/a&gt;, I was winging it. I wasn’t sure what to expect. I didn’t have a real plan and just wanted to see what I would get. I gave Claude vague directions and let it do what it wanted.&lt;/p&gt;

&lt;p&gt;One thing it did was create use JavaScript for the web scraper. I get it kind of assumed that since I used Gatsby for the website. But I really wanted to use Python Scrapy, because I have already created a few scrapers with it in the past. Using it would allow me to correct any wrong turns quickly. I am just learning Gatsby and with AI writing most of my code, one unknown is enough.&lt;/p&gt;

&lt;p&gt;Spoiler alert: by the end of this blog post, I decided using Claude Desktop was not the most efficient way to code this way and found a better tool. One benefit to using Claude desktop though is that if all you have is a Claude Chat subscription, you can use this method to have it write code for you.&lt;/p&gt;

&lt;h2 id=&quot;converting-the-node-scraper-to-python&quot;&gt;Converting the Node Scraper to Python&lt;/h2&gt;

&lt;p&gt;In my last post, I used two chats: one to create a useable blog site out of a default Gatsby installation and one to build a scraper to find writing markets. This time I continued the second chat on the web scraper. I started with this prompt:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I am considering changing the scraper from using JavaScript to using Python and Scrapy with all of the same features.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude responded with the benefits of this change and laid out the structure of the new scraper:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eristoddle-scraper/
├── scrapy.cfg                     # Scrapy project configuration
├── requirements.txt               # Dependencies
├── setup.py                       # For installing as a package
├── eristoddle_scraper/
│   ├── __init__.py
│   ├── items.py                   # Market data schema
│   ├── middlewares.py             # Custom request/response handling
│   ├── pipelines.py               # Processing and storing market data
│   ├── settings.py                # Project settings
│   ├── spiders/                   # Spider implementations
│   │   ├── __init__.py
│   │   ├── duotrope_spider.py     # Duotrope crawler
│   │   ├── submittable_spider.py  # Submittable crawler
│   │   ├── custom_spider.py       # Custom site crawler
│   │   └── firecrawl_spider.py    # FireCrawl integration
│   └── utils/
│       ├── __init__.py
│       ├── extractors.py          # Functions for extracting data
│       ├── formatters.py          # Text formatting utilities
│       └── history.py             # Market update history tracking
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So I let it get to work. I had to watch it and tell it to “Continue” every once in a while and when it was done, it gave me information on the new scraper in enough detail that I had to tell it to continue in the middle. The first part:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-mcp-python-scrapy-overview.png&quot; alt=&quot;claude-mcp-python-scrapy-overview&quot; srcset=&quot;            /assets/resized/480/claude-mcp-python-scrapy-overview.png 480w,            /assets/resized/800/claude-mcp-python-scrapy-overview.png 800w,            /assets/resized/1400/claude-mcp-python-scrapy-overview.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The second part told me how to use the new web scraper and how to extend it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-mcp-python-scrapy-instructions.png&quot; alt=&quot;claude-mcp-python-scrapy-instructions&quot; srcset=&quot;            /assets/resized/480/claude-mcp-python-scrapy-instructions.png 480w,            /assets/resized/800/claude-mcp-python-scrapy-instructions.png 800w,            /assets/resized/1400/claude-mcp-python-scrapy-instructions.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;issues-i-ran-into&quot;&gt;Issues I Ran Into&lt;/h2&gt;

&lt;p&gt;I have learned to expect issues and to QA the changes that Claude makes often. I am currently turning this into a process I use over and over to streamline things, but like I said, I switched to another tool, but some things I learned here are still useful if you want to try using Claude Desktop for this. Here are some issues I had before it actually ran:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;no module named python-dotenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I just told it this happened, and it fixed it.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
The installed reactor &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;twisted.internet.selectreactor.SelectReactor&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; does not match the requested one &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;twisted.internet.asyncioreactor.AsyncioSelectorReactor&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
...

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The complete error was rather long. I pasted the whole thing in and it added missing imports.&lt;/p&gt;

&lt;p&gt;About this time, it figured out it messed some things up, so it started running and testing it itself. It found this error:&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameError: name &apos;unicode&apos; is not defined&lt;/code&gt;. Then fixed it itself. Not sure what triggered this, but there are times I wish it would just do this. But you just have to be ready for it when you are running a Scrapy with Playwright scraper in debug mode, because you will only intermittently have control over the cursor.&lt;/p&gt;

&lt;p&gt;After that the scraper ran, so I tested one job and it didn’t find any data, so I asked about that and promptly got told that I reached the limit of the chat. I had not yet discovered the solution I am currently using yet, so I figured I should…&lt;/p&gt;

&lt;h2 id=&quot;use-projects-when-vibe-coding-with-claude-desktop&quot;&gt;Use Projects When Vibe Coding with Claude Desktop&lt;/h2&gt;

&lt;p&gt;I quickly realized that trying to build this project chat by chat was going to be a pain in the ass, so I created a project for it. First, I exported the two chats I created with the &lt;a href=&quot;https://chromewebstore.google.com/detail/claude-magic-export/egddooecigfdpecpkdpklmejabiionep?hl=en&quot;&gt;Claude Magic Export Chrome extension&lt;/a&gt; and uploaded them as Project Knowledge in the new project. I think there are better ways to export these now and I thought I found a way to just adding existing chats as Project Knowledge, but now I can’t find it and I might be lying. I also created a markdown file with a breakdown of the project and its file structure. I figured I would add more things as I thought of them.&lt;/p&gt;

&lt;h2 id=&quot;more-issues-with-the-scrapy-web-scraper-claude-created&quot;&gt;More Issues with the Scrapy Web Scraper Claude Created&lt;/h2&gt;

&lt;p&gt;So once I created the Claude project, I continued debugging the scraper. When I asked it about the job that wasn’t collecting data, it suggesting adding verbose logging and a bunch of debugging logs, so I went to check out the site before doing all that. It was a single page app. I told it that pure Scrapy might not work for it. I knew &lt;a href=&quot;https://github.com/scrapy-plugins/scrapy-playwright&quot;&gt;adding Playwright&lt;/a&gt; was the answer, but just asked to see what it would say. It came to the same conclusion.&lt;/p&gt;

&lt;p&gt;It wrote the code for that. I ran it again, and it still did not collect any data. It suggested the debugging method again. I looked at the code before doing this and saw the issue. The Scrapy part of the scraper was using different selectors than the Playwright part. When it added the Playwright scraper, it just dumped a bunch of generic selectors in them. It actually took a few messages to get it to see what I was seeing, to the point I asked “You are looking at my code right, with file access?” at one point.&lt;/p&gt;

&lt;p&gt;Then I had to tell it which set of selectors was correct even though it had created both of them. After that runaround, the scraper finally worked in Python and actually scraped data.&lt;/p&gt;

&lt;p&gt;Then I figured out it was only scraping the first page of anything paginated, so in that same new chat I started trying to get it to fix that problem and eventually got told the chat was too long again before I got it fixed.&lt;/p&gt;

&lt;p&gt;After that I limited a chat in the Project to one bug or feature. I also started wondering what I should add to Project Knowledge, so I didn’t start from scratch with every new chat.&lt;/p&gt;

&lt;p&gt;I had it do a few other things that day and realized this was still going to be a lot of work. I was scraping a few different sites and wanted the data I was getting to be normalized.&lt;/p&gt;

&lt;p&gt;And I explore this type of stuff and write articles about it when I have gaps in my freelance writing work and about that time, I got a batch of new writing projects. And by the time I got back to this, I started wondering about using another tool that wouldn’t have as many issues remembering parts of large projects.&lt;/p&gt;

&lt;h2 id=&quot;in-the-end-i-switched-to-claude-code&quot;&gt;In the End, I Switched to Claude Code&lt;/h2&gt;

&lt;p&gt;I actually learned about &lt;a href=&quot;https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview&quot;&gt;Claude Code&lt;/a&gt; from a dialog box on the Claude AI website, so I thought I’d try it. There were not very many tutorials at the time, and I wanted to start a project from scratch with it. Most of what I was reading said it “knows your codebase” and “can help you fix bugs.” This was not telling me what I wanted to know, so I installed it, add money to my Claude API account, and asked it, “Are you only for existing codebases or can you help me build a project from scratch.”&lt;/p&gt;

&lt;p&gt;It was on. I wanted to start with a new project so I would have a new slate and could see what it can do. I do plan on using Claude Code with this project, eventually. So my next article in this category will cover building an Electron desktop app with Claude Code. I have actually made a lot of progress on it already. The app is functional enough to use for what I built it for.&lt;/p&gt;

&lt;p&gt;Also, it is much easier and less haphazard to use for coding than Claude Desktop. I have become a project manager and QA, while it does most of the work. I have a process that allows me to brainstorm, vet, develop, and test new features in a cycle that doesn’t allow many bugs to get in or wrong turns. And this new project already is more complex and has made it farther in about the same amount of time this first project took. And as soon as I get all of my notes together, I’ll write that post. Stay tuned.&lt;/p&gt;
</description>
        <pubDate>Mon, 21 Apr 2025 08:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/vibe-coding-with-claude-desktop-part-2/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/vibe-coding-with-claude-desktop-part-2/</guid>
        
        
        <category>vibe-coding</category>
        
        <category>python</category>
        
        <category>programming</category>
        
      </item>
    
      <item>
        <title>Claude + MCP - &apos;Vibe Coding&apos; Without Specialized IDEs Part 1</title>
        <description>&lt;p&gt;First, I have to say I hate the term “vibe coding”. But I was curious after seeing some of the apps that were built this way. It took a while, because I have had a CoPilot subscription for over a year and while it helps with things, it also is very stupid. My biggest pet peeve is this common cycle (paraphrased):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Me: I get this error on this line&lt;/li&gt;
  &lt;li&gt;CoPilot: Try this code&lt;/li&gt;
  &lt;li&gt;Me: Is your code any different than mine?&lt;/li&gt;
  &lt;li&gt;CoPilot: Yes, I blah, blah, blah…&lt;/li&gt;
  &lt;li&gt;Me: I did a diff and your code is no different&lt;/li&gt;
  &lt;li&gt;CoPilot: Try this code&lt;/li&gt;
  &lt;li&gt;Me: That looks the same also. Can you see the file I added? Do a manual comparison, line by line.&lt;/li&gt;
  &lt;li&gt;CoPilot: You are right, my code is the same as yours.&lt;/li&gt;
  &lt;li&gt;Me: So how does your code fix the error if it is no different?&lt;/li&gt;
  &lt;li&gt;(Cycle starts over again from the beginning)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it took me a while to buy into the hype.&lt;/p&gt;

&lt;p&gt;So for the last two years, I’ve used the free versions of Claude, ChatGPT, and Gemini simultaneously because I wanted to compare the results. And I used them on almost a daily basis. When Claude 3.5 Sonnet came out, in my comparisons and use cases, it quickly became the top tool for me, but then started cutting me off. So after two years of free, I paid for a subscription that day. And 3.7 is even better.&lt;/p&gt;

&lt;p&gt;So I decided to give “vibe coding” a try, but with Claude, so I learned about MCPs. There are plenty of tutorials out there, but they kind of stopped with setting it up. And I wanted to show you how far you could on a project in about 2 hours. You know, a reason why to go through all of this trouble.&lt;/p&gt;

&lt;h2 id=&quot;what-are-model-context-protocol-servers&quot;&gt;What are Model Context Protocol Servers?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://modelcontextprotocol.io/&quot;&gt;Model Context Protocol&lt;/a&gt; allows Claude and other AI chat tools to connect to data sources and external tools. It lets Claude reach out and interact with other stuff. For example, your file system. So instead of adding one file at a time to a chat window, you can add the whole filesystem.&lt;/p&gt;

&lt;p&gt;For this post, I installed two packages which gave me 20 MCP Tools:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/wonderwhy-er/DesktopCommanderMCP&quot;&gt;@wonderwhy-er/desktop-commander:&lt;/a&gt;&lt;/strong&gt; This is server that allows Claude desktop app to execute long-running terminal commands on your computer and manage processes through Model Context Protocol (MCP) + Built on top of MCP Filesystem Server to provide additional search and replace file editing capabilities .&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking&quot;&gt;@modelcontextprotocol/server-sequential-thinking:&lt;/a&gt;&lt;/strong&gt; An MCP server implementation that provides a tool for dynamic and reflective problem-solving through a structured thinking process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-add-mcp-model-context-protocol-servers-to-claude-desktop&quot;&gt;How to Add MCP (Model Context Protocol) Servers to Claude Desktop&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This is not untechnical! But maybe that’s better. Because even though “vibe coding” can speed things up, having some technical knowledge can go a long way.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/vibe-coder-attacked.png&quot; alt=&quot;vibe-coder-attacked&quot; srcset=&quot;            /assets/resized/480/vibe-coder-attacked.png 480w,            /assets/resized/800/vibe-coder-attacked.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I use &lt;a href=&quot;https://github.com/nvm-sh/nvm&quot;&gt;nvm&lt;/a&gt;. So, maybe that why I had so many issues. I started with one tutorial that told me all I had to do was run these commands:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; @smithery/cli &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @wonderwhy-er/desktop-commander &lt;span class=&quot;nt&quot;&gt;--client&lt;/span&gt; claude
npx &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; @smithery/cli@latest &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @smithery-ai/server-sequential-thinking &lt;span class=&quot;nt&quot;&gt;--client&lt;/span&gt; claude
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that didn’t work for me. I got a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Could not attach to MCP server filesystem&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;Then another that told me all I had to do was create a wrapper for nvm and use that. Same error.&lt;/p&gt;

&lt;p&gt;This is what finally worked for me. Run these commands to install the MCP servers globally:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; @modelcontextprotocol/server-sequential-thinking
npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; @wonderwhy-er/desktop-commander
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then open Claude’s settings:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/claude-desktop-settings-menu.png&quot; alt=&quot;claude-desktop-settings-menu&quot; srcset=&quot;            /assets/resized/480/claude-desktop-settings-menu.png 480w,            /assets/resized/800/claude-desktop-settings-menu.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then open the Config file manually by clicking &lt;strong&gt;Edit Config&lt;/strong&gt; in the &lt;strong&gt;Developer&lt;/strong&gt; section:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/edit-claude-desktop-config.png&quot; alt=&quot;edit-claude-desktop-config&quot; srcset=&quot;            /assets/resized/480/edit-claude-desktop-config.png 480w,            /assets/resized/800/edit-claude-desktop-config.png 800w,            /assets/resized/1400/edit-claude-desktop-config.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This will open the folder with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude_desktop_config.json&lt;/code&gt; selected. Open the file and hard code the paths of your node installation and the index file of the dist folder (at least for these two MCP servers) .&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;server-sequential-thinking&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/bin/node&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/lib/node_modules/@modelcontextprotocol/server-sequential-thinking/dist/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;desktop-commander&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/bin/node&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/lib/node_modules/@wonderwhy-er/desktop-commander/dist/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can the location of the node installation you are using by running this command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;which node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which will return something like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/bin/node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YOUR_USERNAME&lt;/code&gt; in the config file with your actual username and the version number with the version number of your node installation. And it worked. Then I needed something to build.&lt;/p&gt;

&lt;h2 id=&quot;the-idea&quot;&gt;The Idea&lt;/h2&gt;

&lt;p&gt;I have had this idea for a new project for a while and just kept putting it on the back burner because I knew it would take some time. It seemed like a good choice for this experiment:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Build a website using a static site generator:&lt;/strong&gt; My blog uses &lt;a href=&quot;https://www.stephanmiller.com/search/?query=jekyll&quot;&gt;Jekyll&lt;/a&gt;. I really don’t care for Ruby and &lt;a href=&quot;https://www.gatsbyjs.com/&quot;&gt;Gatsby&lt;/a&gt; has more features, plugins, and themes. I know JavaScript well but not Gatsby, so there would be a learning curve. Also, go ahead and hack my static site. The data isn’t there.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Create a directory of writing markets:&lt;/strong&gt; I have used Python &lt;a href=&quot;https://scrapy.org/&quot;&gt;Scrapy&lt;/a&gt; often and am good with it, but setting up a scraper for each site does take time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Create a newsletter that notifies subscribers of new markets:&lt;/strong&gt; I have tried building newsletters before. My time is often limited because when there is freelance work I do it, more than 40,000 words this month. This will provide constant weekly content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Profit!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is actually more I plan on doing after that, but that should get and keep things going.&lt;/p&gt;

&lt;h2 id=&quot;the-first-night-coding-with-claude&quot;&gt;The First Night Coding with Claude&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Add your code to a git repo and commit when things work, so if anything goes south, you can revert back to a working state.&lt;/li&gt;
  &lt;li&gt;Claude had trouble with it detecting my node version, so I would ask it if it knows your version of node. If it spits out the wrong value, tell it your actual node version and tell it to use that version when creating files. Or it will generate buggy code, but when I told it the actual version of node it quickly fixed everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first night, I messed with the setup and then only had about a half hour left, so I worked on the Gatsby part of the app. I already had a basic installation but hadn’t made any changes to it. And if you’ve ever done that, you know how basic it is. This my first prompt and I really didn’t expect much:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In /path/my/project/is/in, I am building a Gatsby site. I want a nice looking theme and I want the standard blog+pages type of structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each time it accesses a new folder, it will ask for permission. Sometimes it seems like it asks way too many times but it eventually stops. I actually forgot to take this screenshot, so I created another project to update the theme on my Jekyll blog which is over a decade old. In that case, it actually told me to create another branch in git to work on those changes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/claude-file-system-permissions.png&quot; alt=&quot;claude-file-system-permissions&quot; srcset=&quot;            /assets/resized/480/claude-file-system-permissions.png 480w,            /assets/resized/800/claude-file-system-permissions.png 800w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you allow it to access a folder, its off and running:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/claude-first-response.png&quot; alt=&quot;claude-first-response&quot; srcset=&quot;            /assets/resized/480/claude-first-response.png 480w,            /assets/resized/800/claude-first-response.png 800w,            /assets/resized/1400/claude-first-response.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The I just told it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Continue&lt;/code&gt; a few times. When there was an error, I told it about it and it fixed it. This is where I ran into the node version mismatch, but once I told it my version, it fixed things. When it was done, it told me how to run it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/claude-next-steps.png&quot; alt=&quot;claude-next-steps&quot; srcset=&quot;            /assets/resized/480/claude-next-steps.png 480w,            /assets/resized/800/claude-next-steps.png 800w,            /assets/resized/1400/claude-next-steps.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So I wasn’t sure what to expect but here’s what I got when I ran it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/gatsby-site-top.png&quot; alt=&quot;gatsby-site-top&quot; srcset=&quot;            /assets/resized/480/gatsby-site-top.png 480w,            /assets/resized/800/gatsby-site-top.png 800w,            /assets/resized/1400/gatsby-site-top.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/gatsby-site-bottom.png&quot; alt=&quot;gatsby-site-bottom&quot; srcset=&quot;            /assets/resized/480/gatsby-site-bottom.png 480w,            /assets/resized/800/gatsby-site-bottom.png 800w,            /assets/resized/1400/gatsby-site-bottom.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A little bit bright and primary colored for me, but a good start. So I told it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I want to make the colors in the theme more mellow. Right now they are bold and primary colors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not much of a prompt, but it gave me this, which is better. I will tweak it later, but I wanted to get onto other things:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/gatsby-new-theme.png&quot; alt=&quot;gatsby-new-theme&quot; srcset=&quot;            /assets/resized/480/gatsby-new-theme.png 480w,            /assets/resized/800/gatsby-new-theme.png 800w,            /assets/resized/1400/gatsby-new-theme.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also told it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What if in the future, I wanted to scrape some data from websites and use that data to generate pages. These will be writing markets, so I would probably want to create a different type of page for them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After some back and forth on what format I wanted to store each market as, I choose a JSON file per market and got this and the filters and search work well. It just added that without me asking for it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/gatsby-site-writing-markets.png&quot; alt=&quot;gatsby-site-writing-markets&quot; srcset=&quot;            /assets/resized/480/gatsby-site-writing-markets.png 480w,            /assets/resized/800/gatsby-site-writing-markets.png 800w,            /assets/resized/1400/gatsby-site-writing-markets.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-second-night&quot;&gt;The Second Night&lt;/h2&gt;

&lt;p&gt;The second night, I also had around an hour. This night I wanted to create a scraper to provide data for the site. This was my first prompt:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I am building a gatsby project here: /my/project/directory for a blog and to list writing markets. You can see the structure in /my/project/directory/src/data/markets/sample-market.json. That may have to change as I go, as well as its templates. But for now I want to get started on a webscraper project to generate the json files for the gatsby site in /my/project/directory/data. I am not sure what to use for that just yet. I want to be able to eventually run it on cron.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I gave me a project summary and asked me if I wanted to continue.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/claude-web-scraper-plan.png&quot; alt=&quot;claude-web-scraper-plan&quot; srcset=&quot;            /assets/resized/480/claude-web-scraper-plan.png 480w,            /assets/resized/800/claude-web-scraper-plan.png 800w,            /assets/resized/1400/claude-web-scraper-plan.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I will probably see if it will refactor this with &lt;a href=&quot;https://scrapy.org/&quot;&gt;Scrapy&lt;/a&gt; later and see what happens, but I told it that was find and all I did after that was tell it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;continue&lt;/code&gt; 4 or 5 times and here is the final result:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/claude-desktop-vibe-coding-part-1/claude-web-scraper-project-structure.png&quot; alt=&quot;claude-web-scraper-project-structure&quot; srcset=&quot;            /assets/resized/480/claude-web-scraper-project-structure.png 480w,            /assets/resized/800/claude-web-scraper-project-structure.png 800w,            /assets/resized/1400/claude-web-scraper-project-structure.png 1400w,    &quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And it did kind of work. I didn’t guide it very much and it was a good start. It scraped some markets and put it in the folder in JSON format, but only about 8. But like I said, I wanted to see what it would do without much guidance.&lt;/p&gt;

&lt;h2 id=&quot;future-plans&quot;&gt;Future Plans&lt;/h2&gt;

&lt;p&gt;I am actually going to work on this a little more tonight. Some things I’m thinking of include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Eventually tweaking the theme. Putting it off because I know it can be a time suck when I am not really sure what I want.&lt;/li&gt;
  &lt;li&gt;Change the scraper to use Python and Scrapy so I know how to work with it better.&lt;/li&gt;
  &lt;li&gt;Create dockerfiles for each project, especially the Gatsby one. A custom Docker image for my Jekyll blog makes deployment quick.&lt;/li&gt;
  &lt;li&gt;Find out what &lt;a href=&quot;https://www.docker.com/blog/the-model-context-protocol-simplifying-building-ai-apps-with-anthropic-claude-desktop-and-docker/&quot;&gt;this&lt;/a&gt; is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It still has a long way to go but this is only part one and I’ll write about it as I go.&lt;/p&gt;

&lt;p&gt;Part 2 is here: &lt;a href=&quot;/vibe-coding-with-claude-desktop-part-2/&quot;&gt;Vibe Coding With Claude Desktop and MCP Part 2 - Switching to Scrapy&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 31 Mar 2025 07:00:00 -0500</pubDate>
        <link>https://www.stephanmiller.com/using-mcps-with-claude-desktop/</link>
        <guid isPermaLink="true">https://www.stephanmiller.com/using-mcps-with-claude-desktop/</guid>
        
        
        <category>vibe-coding</category>
        
        <category>programming</category>
        
      </item>
    
  </channel>
</rss>
