<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Stephan Miller</title>
    <description>The latest articles on Forem by Stephan Miller (@eristoddle).</description>
    <link>https://forem.com/eristoddle</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18795%2F5f6c41b8-6033-4887-937a-2ebdfe623d2e.jpeg</url>
      <title>Forem: Stephan Miller</title>
      <link>https://forem.com/eristoddle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eristoddle"/>
    <language>en</language>
    <item>
      <title>I Burned Out on Vibe Coding, Came Back, and Rewrote Everything</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Sun, 08 Feb 2026 07:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/i-burned-out-on-vibe-coding-came-back-and-rewrote-everything-l6i</link>
      <guid>https://forem.com/eristoddle/i-burned-out-on-vibe-coding-came-back-and-rewrote-everything-l6i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zne0f49kpebtq4tpg8g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zne0f49kpebtq4tpg8g.jpg" alt="AI-assisted development" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&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;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your Vibe-Coded Apps Are Prototypes (And That’s Fine)&lt;/li&gt;
&lt;li&gt;Making “Adding Features” the Feature&lt;/li&gt;
&lt;li&gt;Building Bottom-Up with Verdent and Claude Code&lt;/li&gt;
&lt;li&gt;The 60 Missing APIs&lt;/li&gt;
&lt;li&gt;Making Plans That Any AI Agent Can Execute&lt;/li&gt;
&lt;li&gt;The Same Pattern, Different Project&lt;/li&gt;
&lt;li&gt;What Changed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&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="https://dev.to/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03"&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&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&gt;
  
  
  Building Bottom-Up with Verdent and Claude Code
&lt;/h2&gt;

&lt;p&gt;I used &lt;a href="https://www.verdent.ai/" rel="noopener noreferrer"&gt;Verdent&lt;/a&gt; to build the base application. If you read &lt;a href="https://dev.to/eristoddle/verdent-ai-when-your-ai-coding-assistant-finishes-before-you-can-get-coffee-4e76"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&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;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&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&gt;
  
  
  Making Plans That Any AI Agent Can Execute
&lt;/h2&gt;

&lt;p&gt;This is the part that parallels &lt;a href="https://mitchellh.com/writing/my-ai-adoption-journey" rel="noopener noreferrer"&gt;Mitchell Hashimoto'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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&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;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&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&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="https://www.stephanmiller.com/category/vibe-coding/" rel="noopener noreferrer"&gt;vibe coding&lt;/a&gt;. I’m just vibing with more structure now and calling it &lt;a href="https://www.stephanmiller.com/category/ai-assisted-development/" rel="noopener noreferrer"&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>
      <category>aiassisteddevelopmen</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Verdent AI - When Your AI Coding Assistant Finishes Before You Can Get Coffee</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Tue, 23 Sep 2025 13:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/verdent-ai-when-your-ai-coding-assistant-finishes-before-you-can-get-coffee-4e76</link>
      <guid>https://forem.com/eristoddle/verdent-ai-when-your-ai-coding-assistant-finishes-before-you-can-get-coffee-4e76</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewhtymj27nrppg1jmcu1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewhtymj27nrppg1jmcu1.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&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="https://dev.to/eristoddle/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work-40e4"&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59g8hfv91i3o4qi4cced.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59g8hfv91i3o4qi4cced.png" alt="Obsidian Cleaner Plugin" width="800" height="388"&gt;&lt;/a&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9dsumfm5fm9weqk6g0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9dsumfm5fm9weqk6g0z.png" alt="Obsidian 3D Tag Explorer Plugin" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8ys3fzpgpjba1103doc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8ys3fzpgpjba1103doc.jpg" alt="Verdent Chat Window" width="800" height="1492"&gt;&lt;/a&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&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbd7rz4f8i62menl7w8qa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbd7rz4f8i62menl7w8qa.jpg" alt="BookForge Done In One Commit" width="800" height="298"&gt;&lt;/a&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&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&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&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&gt;
  
  
  Verdent Features: The Good Stuff That Actually Works
&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcl6ottz95dx7512vvodr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcl6ottz95dx7512vvodr.jpg" alt="Verdent Plan Mode" width="664" height="1260"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpyjk83543l4m7fw8hw7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpyjk83543l4m7fw8hw7.jpg" alt="Verdent Follow Up Questions" width="612" height="740"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6q192qx09msn60z6xfd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6q192qx09msn60z6xfd.jpg" alt="Verdent Project Plan" width="800" height="500"&gt;&lt;/a&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&gt;
  
  
  Rules System
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2bhb9hfz6c9t8iu0aml.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2bhb9hfz6c9t8iu0aml.jpg" alt="Verdent Rules" width="644" height="1182"&gt;&lt;/a&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&gt;
  
  
  Subagents
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0gpz3s7xri273k1r2d2w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0gpz3s7xri273k1r2d2w.jpg" alt="Verdent Subagents" width="800" height="246"&gt;&lt;/a&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&gt;
  
  
  MCP Support
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F612pm1umov4wech9tqcq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F612pm1umov4wech9tqcq.jpg" alt="Verdent MCP Support" width="670" height="550"&gt;&lt;/a&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&gt;
  
  
  The Pricing Reality Check
&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&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&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="https://verdent.ai/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and find the VS code plugin or Verdent Deck &lt;a href="https://www.verdent.ai/download" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>verdent</category>
    </item>
    <item>
      <title>The Great Vibe Coding Experiment - How I Built 15 Projects with AI in My Spare Time</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Mon, 15 Sep 2025 07:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/the-great-vibe-coding-experiment-how-i-built-15-projects-with-ai-in-my-spare-time-275o</link>
      <guid>https://forem.com/eristoddle/the-great-vibe-coding-experiment-how-i-built-15-projects-with-ai-in-my-spare-time-275o</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcl7xf3z1genj6bxtrld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcl7xf3z1genj6bxtrld.png" alt="Vibe Coding" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29gdtyt1i22s4gj3orqi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29gdtyt1i22s4gj3orqi.jpg" alt="Github Commit Chart" width="800" height="150"&gt;&lt;/a&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&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;When Vibe Coding Becomes Automated Spec-Driven Development&lt;/li&gt;
&lt;li&gt;
The Obsidian Plugin Empire

&lt;ul&gt;
&lt;li&gt;Apple Books Highlights Plugin: Hacking SQLite&lt;/li&gt;
&lt;li&gt;Joplin Portal: My First Test of Kiro&lt;/li&gt;
&lt;li&gt;Daily Note Prompts: An Extension I Am Using&lt;/li&gt;
&lt;li&gt;Tag Explorer 3D: Testing a Shiny New Tool&lt;/li&gt;
&lt;li&gt;Attachment Cleaner: Simple But Necessary&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

The Bigger Fish: Apps That Do Real Shit

&lt;ul&gt;
&lt;li&gt;Gatsby Site with Scraper: The Abandoned First Project&lt;/li&gt;
&lt;li&gt;GitWrite: When Jules Met Agentic Project Manager&lt;/li&gt;
&lt;li&gt;EmberText: My First Claude Code Experiment&lt;/li&gt;
&lt;li&gt;MDQuery: Fed Up with Proprietary Tools&lt;/li&gt;
&lt;li&gt;AutoVibe: The Meta Project&lt;/li&gt;
&lt;li&gt;AutoVibe Template: The Stop-Gap Solution&lt;/li&gt;
&lt;li&gt;ShopBoth: Testing the Template (and Learning Hard Lessons)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Tool Whose Name Shall Not Be Spoken&lt;/li&gt;

&lt;li&gt;How These Projects Work Together&lt;/li&gt;

&lt;li&gt;

What I Learned About AI-Assisted Development

&lt;ul&gt;
&lt;li&gt;Each AI Tool Has Its Own Personality&lt;/li&gt;
&lt;li&gt;The Process That Emerged&lt;/li&gt;
&lt;li&gt;Why 15 Projects Makes Sense (Sort Of)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;What’s Next: The AI Development Army&lt;/li&gt;

&lt;li&gt;Conclusion: Embrace the Chaos&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&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&gt;
  
  
  The Obsidian Plugin Empire
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apple Books Highlights Plugin: Hacking SQLite
&lt;/h3&gt;

&lt;p&gt;I built this &lt;a href="https://github.com/eristoddle/apple-books-annotation-import" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; with Claude Desktop and MCPs, documented the whole process in &lt;a href="https://dev.to/eristoddle/creating-an-obsidian-plugin-with-claude-ai-gaj"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrfsvm2cqtxensrldr32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrfsvm2cqtxensrldr32.png" alt="Apple Book Annotation Import" width="800" height="957"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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="https://github.com/eristoddle/joplin-portal" rel="noopener noreferrer"&gt;https://github.com/eristoddle/joplin-portal&lt;/a&gt;. &lt;a href="https://dev.to/eristoddle/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work-40e4"&gt;Kiro did most of the work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2vl52sfhg1ktis4ahcc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2vl52sfhg1ktis4ahcc.jpg" alt="Joplin Portal Sidebar" width="800" height="1391"&gt;&lt;/a&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow12n4pj9ims4uwqae9h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow12n4pj9ims4uwqae9h.jpg" alt="Obsidian Daily Prompt Settings" width="800" height="944"&gt;&lt;/a&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9dsumfm5fm9weqk6g0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9dsumfm5fm9weqk6g0z.png" alt="Obsidian Tag Explorer 3D" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59g8hfv91i3o4qi4cced.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59g8hfv91i3o4qi4cced.png" alt="Obsidian Attachment Cleaner" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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&gt;
  
  
  Gatsby Site with Scraper: The Abandoned First Project
&lt;/h3&gt;

&lt;p&gt;This was my &lt;a href="https://dev.to/eristoddle/claude-mcps-vibe-coding-without-specialized-ides-part-1-1hmd"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi40ktvl05t4hj1wf4rhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi40ktvl05t4hj1wf4rhm.png" alt="AI Generated Gatsby Site" width="800" height="1110"&gt;&lt;/a&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&gt;
  
  
  GitWrite: When Jules Met Agentic Project Manager
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/eristoddle/git-write" rel="noopener noreferrer"&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&gt;
  
  
  EmberText: My First Claude Code Experiment
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ha0dotuo887ok6513r6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ha0dotuo887ok6513r6.png" alt="EmberText Project View" width="800" height="642"&gt;&lt;/a&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&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="https://github.com/eristoddle/mdquery" rel="noopener noreferrer"&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="https://shmck.substack.com/p/claude-code-framework-wars" rel="noopener noreferrer"&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="https://github.com/eristoddle/mdquery/blob/main/docs/claude-desktop-prompts.md" rel="noopener noreferrer"&gt;prompts the AI tools had put in the documentation&lt;/a&gt;. And it spit out an &lt;a href="///downloads/LlmCodingNotes.pdf"&gt;80 page document&lt;/a&gt;.&lt;/p&gt;

&lt;h3&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2F2025%2Fautovibe-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2F2025%2Fautovibe-dashboard.png" alt="AutoVibe Dashboard" width="800" height="982"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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="https://github.com/eristoddle/autovibe-backlog-md-template" rel="noopener noreferrer"&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&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&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&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&gt;
  
  
  What I Learned About AI-Assisted Development
&lt;/h2&gt;

&lt;h3&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&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&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&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&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>
      <category>vibecoding</category>
    </item>
    <item>
      <title>How I Built Two Obsidian Plugins While Kiro AI Did Most of the Work</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Fri, 22 Aug 2025 07:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work-40e4</link>
      <guid>https://forem.com/eristoddle/how-i-built-two-obsidian-plugins-while-kiro-ai-did-most-of-the-work-40e4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx236i76wouhdk8d7g3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx236i76wouhdk8d7g3o.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://dev.to/eristoddle/creating-an-obsidian-plugin-with-claude-ai-gaj"&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="https://dev.to/eristoddle/jules-ai-the-currently-free-coding-assistant-that-cant-follow-directions-but-gets-shit-done-33k3"&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="https://dev.to/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03"&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="https://dev.to/eristoddle/i-tried-to-upgrade-my-blog-with-ai-project-management-and-everything-went-to-hell-but-the-process-2e43-temp-slug-8610687"&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&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is Kiro and Why I’m Using It&lt;/li&gt;
&lt;li&gt;
Plugin #1: Daily Note Prompts - The Spec-Driven Experience

&lt;ul&gt;
&lt;li&gt;Requirement 1&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;My Obsidian Development Process&lt;/li&gt;

&lt;li&gt;Plugin #2: Joplin Portal - When AI Tools Hit a Wall&lt;/li&gt;

&lt;li&gt;

Lessons Learned

&lt;ul&gt;
&lt;li&gt;Spec-driven development workflow&lt;/li&gt;
&lt;li&gt;When to trust AI vs when to investigate yourself&lt;/li&gt;
&lt;li&gt;Managing AI tool limitations and daily limits&lt;/li&gt;
&lt;li&gt;The value of simple projects for learning new tools&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Kiro and Why I’m Using It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kiro.dev/blog/introducing-kiro/" rel="noopener noreferrer"&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnnl8k0yl9i6z5i0y9bs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnnl8k0yl9i6z5i0y9bs.jpg" alt="Kiro Chat Drawer" width="800" height="1768"&gt;&lt;/a&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="https://github.com/eristoddle/obsidian-daily-note-prompts/blob/main/.kiro/specs/obsidian-daily-prompts/requirements.md" rel="noopener noreferrer"&gt;requirements file&lt;/a&gt; has entries that look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&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&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="https://github.com/eristoddle/obsidian-daily-note-prompts/blob/main/.kiro/specs/obsidian-daily-prompts/design.md" rel="noopener noreferrer"&gt;design file&lt;/a&gt; specifies things like data models, service interfaces, and other architectural details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg999gf5ziy4pcec1y3uu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg999gf5ziy4pcec1y3uu.jpg" alt="Kiro Design File" width="800" height="735"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final file it creates is the &lt;a href="https://github.com/eristoddle/obsidian-daily-note-prompts/blob/main/.kiro/specs/obsidian-daily-prompts/tasks.md" rel="noopener noreferrer"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjrzxo8tzoax8g4rixt1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjrzxo8tzoax8g4rixt1.jpg" alt="Kiro Tasks File" width="800" height="634"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F061vtr6rxkakih7891fp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F061vtr6rxkakih7891fp.jpg" alt="Kiro Implementation Plan" width="800" height="1125"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falwqpybcy21ps6a601sv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falwqpybcy21ps6a601sv.jpg" alt="Trusting Kiro Commands" width="800" height="501"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow12n4pj9ims4uwqae9h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow12n4pj9ims4uwqae9h.jpg" alt="Obsidian Daily Prompt Settings" width="800" height="944"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiymdoxyszl5axbcybwf2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiymdoxyszl5axbcybwf2.jpg" alt="Obsidian Daily Prompt Edit Prompts" width="800" height="1688"&gt;&lt;/a&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="https://github.com/eristoddle/obsidian-daily-note-prompts" rel="noopener noreferrer"&gt;Obsidian Daily Note Prompts&lt;/a&gt;&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&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8z5hs30agzvolyiiq1gd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8z5hs30agzvolyiiq1gd.jpg" alt="Kiro Features Prompt" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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="https://joplinapp.org/help/api/references/rest_api/" rel="noopener noreferrer"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2vl52sfhg1ktis4ahcc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2vl52sfhg1ktis4ahcc.jpg" alt="Joplin Portal Sidebar" width="800" height="1391"&gt;&lt;/a&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="https://github.com/eristoddle/joplin-portal" rel="noopener noreferrer"&gt;Obsidian Joplin Portal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7vltbfvx1n0dstilr85.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7vltbfvx1n0dstilr85.jpg" alt="Kiro Specs, Hooks, Steering, and MCP" width="790" height="2484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&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&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&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&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&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>
      <category>vibecoding</category>
      <category>obsidian</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Jules AI - The (Currently) Free Coding Assistant That Can't Follow Directions But Gets Shit Done</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Tue, 10 Jun 2025 13:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/jules-ai-the-currently-free-coding-assistant-that-cant-follow-directions-but-gets-shit-done-33k3</link>
      <guid>https://forem.com/eristoddle/jules-ai-the-currently-free-coding-assistant-that-cant-follow-directions-but-gets-shit-done-33k3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4hh44z0chvfa8bmzqnc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4hh44z0chvfa8bmzqnc.png" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevline3hrpysdmps6uen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevline3hrpysdmps6uen.png" alt="obsidian-plugin-github-issues" width="800" height="292"&gt;&lt;/a&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0fu7sjoprmthq42z967.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0fu7sjoprmthq42z967.png" alt="obsidian-plugin-future-enhancements" width="800" height="543"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqnup91vj1pbim7q6hqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqnup91vj1pbim7q6hqw.png" alt="jules-obsidian-plugin-branch" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&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;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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91ifg2dalj4t6p4zm0og.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91ifg2dalj4t6p4zm0og.png" alt="ebook-selection-modal" width="800" height="842"&gt;&lt;/a&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&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="https://github.com/google-labs-code/jules-awesome-list" rel="noopener noreferrer"&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: &lt;a href="https://github.com/eristoddle/apple-books-annotation-import/issues" rel="noopener noreferrer"&gt;https://github.com/eristoddle/apple-books-annotation-import/issues&lt;/a&gt;. 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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpci5s2f4utp2mt4abhn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpci5s2f4utp2mt4abhn.png" alt="jule-web-interface" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ykxjvnrqe99ae1jiq56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ykxjvnrqe99ae1jiq56.png" alt="jules-cover-fix-plan" width="800" height="287"&gt;&lt;/a&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&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: &lt;a href="https://github.com/eristoddle/apple-books-annotation-import/issues/5" rel="noopener noreferrer"&gt;https://github.com/eristoddle/apple-books-annotation-import/issues/5&lt;/a&gt;. 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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4az4upl978wms13qsati.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4az4upl978wms13qsati.png" alt="jules-annotation-color-message-two" width="800" height="498"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfjye3fwyv6n12bxlj7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfjye3fwyv6n12bxlj7o.png" alt="obsidian-annotation-colors" width="800" height="1381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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&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&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&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&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>
      <category>llmcoding</category>
      <category>vibecoding</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Creating an Obsidian Plugin with Claude AI</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Mon, 02 Jun 2025 13:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/creating-an-obsidian-plugin-with-claude-ai-gaj</link>
      <guid>https://forem.com/eristoddle/creating-an-obsidian-plugin-with-claude-ai-gaj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3d0eqmeljwetrhkifnmv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3d0eqmeljwetrhkifnmv.jpg" alt="Image description" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this post, I took a break from the &lt;a href="https://dev.to/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03"&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="https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking" rel="noopener noreferrer"&gt;sequential thinking&lt;/a&gt; and &lt;a href="https://github.com/wonderwhy-er/DesktopCommanderMCP" rel="noopener noreferrer"&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&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="https://dev.to/eristoddle/exporting-mac-osx-book-highlights-into-an-obsidian-vault-or-markdown-files-40lg"&gt;Python script to import Apple Books annotations&lt;/a&gt;, using example from sources like this &lt;a href="https://github.com/davidfor/calibre-annotations/blob/master/readers/_iBooks.py" rel="noopener noreferrer"&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&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&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&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&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo728yja1b1tgi26dhgar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo728yja1b1tgi26dhgar.png" alt="new-obsidian-annotations-md-template" width="800" height="1327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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="https://gist.github.com/eristoddle/5a8e7dd0597d09d00aa5de066788c303" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp01dvp6aoft0hfj1nbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp01dvp6aoft0hfj1nbn.png" alt="claude-generate-obsidian-plugin" width="800" height="888"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrfsvm2cqtxensrldr32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrfsvm2cqtxensrldr32.png" alt="obsidian-annotation-import-settings" width="800" height="957"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pc1pk1h1lep81hcu2qz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pc1pk1h1lep81hcu2qz.png" alt="claude-getting-my-name-wrong" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jxsexvhopwy4l1euxbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jxsexvhopwy4l1euxbh.png" alt="fuck-you-google" width="800" height="532"&gt;&lt;/a&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&gt;
  
  
  SQLite Library Issues
&lt;/h4&gt;

&lt;p&gt;A big hurdle was database access. Claude initially tried &lt;code&gt;better-sqlite3&lt;/code&gt; but ran into Electron compatibility issues. We switched to &lt;code&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&gt;sql.js&lt;/code&gt; with proper WASM file handling in the Obsidian environment.&lt;/p&gt;

&lt;h4&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&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&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&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&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&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&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&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&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&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72papzemlnrrmm8b574x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72papzemlnrrmm8b574x.png" alt="claude-create-obsidian-plugin-results" width="800" height="2064"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned This Time
&lt;/h2&gt;

&lt;h3&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&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&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vh0i4p0shp2n3jhndre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vh0i4p0shp2n3jhndre.png" alt="obsidian-book-note-example" width="800" height="1210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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="https://github.com/TfTHacker/obsidian42-brat" rel="noopener noreferrer"&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&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&gt;latest&lt;/code&gt; version from the dropdown.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&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="https://dev.to/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03"&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>
      <category>vibecoding</category>
      <category>obsidian</category>
      <category>javascript</category>
      <category>python</category>
    </item>
    <item>
      <title>Building an Electron App from Scratch with Claude Code</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Tue, 20 May 2025 12:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03</link>
      <guid>https://forem.com/eristoddle/building-an-electron-app-from-scratch-with-claude-code-5c03</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fey98j5g9kkhxw3u8rn9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fey98j5g9kkhxw3u8rn9v.png" alt="Image description" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&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="https://dev.to/eristoddle/claude-mcps-vibe-coding-without-specialized-ides-part-1-1hmd"&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&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vibe Coding with Claude Desktop and MCP&lt;/li&gt;
&lt;li&gt;Features of Claude Code that Made Me Switch&lt;/li&gt;
&lt;li&gt;
Installing and Using Claude Code

&lt;ul&gt;
&lt;li&gt;Requirements&lt;/li&gt;
&lt;li&gt;Installing and Initializing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Building a Project from Scratch with Claude Code&lt;/li&gt;

&lt;li&gt;The Pain of Refactoring with Claude Code&lt;/li&gt;

&lt;li&gt;

My Current &lt;code&gt;CLAUDE.md&lt;/code&gt; file and Coding Process

&lt;ul&gt;
&lt;li&gt;How to Use This File&lt;/li&gt;
&lt;li&gt;Project Overview&lt;/li&gt;
&lt;li&gt;Key Requirements&lt;/li&gt;
&lt;li&gt;Important References&lt;/li&gt;
&lt;li&gt;Code Style Guidelines&lt;/li&gt;
&lt;li&gt;Project SDLC&lt;/li&gt;
&lt;li&gt;Build/Lint/Test Commands&lt;/li&gt;
&lt;li&gt;Development Status&lt;/li&gt;
&lt;li&gt;Next Development Steps&lt;/li&gt;
&lt;li&gt;QA Checklist&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Lessons I Learned Using Claude Code&lt;/li&gt;

&lt;li&gt;Features of Claude Code I Haven’t Tried Yet&lt;/li&gt;

&lt;li&gt;Claude Code Tips From Others I Plan on Trying&lt;/li&gt;

&lt;li&gt;

What Claude Code Built: Screenshots of My app

&lt;ul&gt;
&lt;li&gt;Dashboard&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;li&gt;Writing Interface&lt;/li&gt;
&lt;li&gt;Character Relationship Graph&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

My Journey with Claude Code Continues

&lt;ul&gt;
&lt;li&gt;What I’ve Learned&lt;/li&gt;
&lt;li&gt;What’s Next&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vibe Coding with Claude Desktop and MCP
&lt;/h2&gt;

&lt;p&gt;When I was &lt;a href="https://dev.to/eristoddle/claude-mcps-vibe-coding-without-specialized-ides-part-1-1hmd"&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="https://dev.to/eristoddle/vibe-coding-with-claude-desktop-and-mcp-part-2-switching-to-scrapy-elb"&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="https://docs.anthropic.com/en/docs/claude-code/overview" rel="noopener noreferrer"&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&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&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&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&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="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;git&lt;/a&gt; 2.23+ (optional)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cli.github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://gitlab.com/gitlab-org/cli" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; CLI for PR workflows (optional)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/BurntSushi/ripgrep?tab=readme-ov-file#installation" rel="noopener noreferrer"&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&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&gt;
  
  
  Installing and Initializing
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @anthropic-ai/claude-code

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;claude

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

&lt;/div&gt;



&lt;p&gt;After Claude Code started, I ran the following command to generate the &lt;code&gt;CLAUDE.md&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/init

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

&lt;/div&gt;



&lt;h2&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&gt;CLAUDE.md&lt;/code&gt; with the project details.&lt;/p&gt;

&lt;h2&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&gt;
  
  
  My Current &lt;code&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&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&gt;CLAUDE.md&lt;/code&gt; file currently, which has changed often and will continue to:&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Use This File
&lt;/h3&gt;

&lt;p&gt;When I decided to refactor the &lt;code&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&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&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&gt;
  
  
  Important References
&lt;/h3&gt;

&lt;p&gt;I had everything in my &lt;code&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="https://docs.anthropic.com/en/docs/claude-code/memory#claude-md-imports" rel="noopener noreferrer"&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&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&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&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&gt;
  
  
  Build/Lint/Test Commands
&lt;/h3&gt;

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

&lt;h3&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&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&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&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="https://docs.anthropic.com/en/docs/claude-code/tutorials#use-extended-thinking" rel="noopener noreferrer"&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&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="https://docs.anthropic.com/en/docs/claude-code/overview" rel="noopener noreferrer"&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&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="https://docs.anthropic.com/en/docs/claude-code/tutorials#use-claude-code-as-an-mcp-server" rel="noopener noreferrer"&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="https://docs.anthropic.com/en/docs/claude-code/tutorials#create-custom-slash-commands" rel="noopener noreferrer"&gt;Create custom slash commands&lt;/a&gt;:&lt;/strong&gt; You can create a &lt;code&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&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="https://www.reddit.com/r/ClaudeAI/comments/1ko5pxk/claude_code_is_a_beast_tips_from_a_week_of/" rel="noopener noreferrer"&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="https://repomix.com/" rel="noopener noreferrer"&gt;repomix&lt;/a&gt; or &lt;a href="https://github.com/mufeedvh/code2prompt" rel="noopener noreferrer"&gt;code2prompt&lt;/a&gt;:&lt;/strong&gt; The refactoring process was a pain. Fixing bugs was a pain. &lt;a href="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" rel="noopener noreferrer"&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&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqatg649thtn2uvi871dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqatg649thtn2uvi871dw.png" alt="embertext-workbench" width="800" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l5b8680mz5gism3vnj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l5b8680mz5gism3vnj3.png" alt="embertext-settings" width="800" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ha0dotuo887ok6513r6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ha0dotuo887ok6513r6.png" alt="embertext-project" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbpolr5n8x8qjgmzysej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbpolr5n8x8qjgmzysej.png" alt="embertext-relationships" width="800" height="746"&gt;&lt;/a&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&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&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&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>
      <category>llmcoding</category>
      <category>vibecoding</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Vibe Coding With Claude Desktop and MCP Part 2 - Switching to Scrapy</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Mon, 21 Apr 2025 13:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/vibe-coding-with-claude-desktop-and-mcp-part-2-switching-to-scrapy-elb</link>
      <guid>https://forem.com/eristoddle/vibe-coding-with-claude-desktop-and-mcp-part-2-switching-to-scrapy-elb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdu0yimn0ulyo0kwrsyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdu0yimn0ulyo0kwrsyk.png" alt="Image description" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my first &lt;a href="https://dev.to/eristoddle/claude-mcps-vibe-coding-without-specialized-ides-part-1-1hmd"&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&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&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;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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vo297lom3s0jxyn5lfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vo297lom3s0jxyn5lfn.png" alt="claude-mcp-python-scrapy-overview" width="800" height="1274"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2F2025%2Fclaude-mcp-python-scrapy-instructions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2F2025%2Fclaude-mcp-python-scrapy-instructions.png" alt="claude-mcp-python-scrapy-instructions" width="800" height="2383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;no module named python-dotenv

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
The installed reactor (twisted.internet.selectreactor.SelectReactor) does not match the requested one (twisted.internet.asyncioreactor.AsyncioSelectorReactor)
...

&lt;/code&gt;&lt;/pre&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&gt;NameError: name 'unicode' 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&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="https://chromewebstore.google.com/detail/claude-magic-export/egddooecigfdpecpkdpklmejabiionep?hl=en" rel="noopener noreferrer"&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&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="https://github.com/scrapy-plugins/scrapy-playwright" rel="noopener noreferrer"&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&gt;
  
  
  In the End, I Switched to Claude Code
&lt;/h2&gt;

&lt;p&gt;I actually learned about &lt;a href="https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview" rel="noopener noreferrer"&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>
      <category>vibecoding</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Claude + MCPs - 'Vibe Coding' Without Specialized IDEs Part 1</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Mon, 31 Mar 2025 12:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/claude-mcps-vibe-coding-without-specialized-ides-part-1-1hmd</link>
      <guid>https://forem.com/eristoddle/claude-mcps-vibe-coding-without-specialized-ides-part-1-1hmd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1li26jyu2v0nbht4j09c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1li26jyu2v0nbht4j09c.jpg" alt="Image description" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&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&gt;
  
  
  What are Model Context Protocol Servers?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&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="https://github.com/wonderwhy-er/DesktopCommanderMCP" rel="noopener noreferrer"&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="https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking" rel="noopener noreferrer"&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywa6vmf44aypywfcfquf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywa6vmf44aypywfcfquf.png" alt="vibe-coder-attacked" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx -y @smithery/cli install @wonderwhy-er/desktop-commander --client claude
npx -y @smithery/cli@latest install @smithery-ai/server-sequential-thinking --client claude

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

&lt;/div&gt;



&lt;p&gt;And that didn’t work for me. I got a &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @modelcontextprotocol/server-sequential-thinking
npm install -g @wonderwhy-er/desktop-commander

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

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhngfxrxjpb6ecjeu3upj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhngfxrxjpb6ecjeu3upj.png" alt="claude-desktop-settings-menu" width="" height=""&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblwg4e08n5w8843ajhne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblwg4e08n5w8843ajhne.png" alt="edit-claude-desktop-config" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open the folder with &lt;code&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mcpServers": {
    "server-sequential-thinking": {
      "command": "/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/bin/node",
      "args": [
        "/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/lib/node_modules/@modelcontextprotocol/server-sequential-thinking/dist/index.js"
      ]
    },
    "desktop-commander": {
      "command": "/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/bin/node",
      "args": [
        "/Users/YOUR_USERNAME/.nvm/versions/node/v20.16.0/lib/node_modules/@wonderwhy-er/desktop-commander/dist/index.js"
      ]
    }
  }
}

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;which node

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&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;p&gt;Just replace &lt;code&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&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="https://www.stephanmiller.com/search/?query=jekyll" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;. I really don’t care for Ruby and &lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&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="https://scrapy.org/" rel="noopener noreferrer"&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&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv7pcacb9m6kma0tdpsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv7pcacb9m6kma0tdpsq.png" alt="claude-file-system-permissions" width="800" height="381"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0jyt9eap8yvnftxn1tx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0jyt9eap8yvnftxn1tx.png" alt="claude-first-response" width="800" height="2166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The I just told it to &lt;code&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ns6x8lsr3mvgic5p5ot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ns6x8lsr3mvgic5p5ot.png" alt="claude-next-steps" width="800" height="748"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskp1x28mf2tipw8uewfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskp1x28mf2tipw8uewfg.png" alt="gatsby-site-top" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iyxs3l7vtmlada5pvd9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iyxs3l7vtmlada5pvd9.png" alt="gatsby-site-bottom" width="800" height="531"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi40ktvl05t4hj1wf4rhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi40ktvl05t4hj1wf4rhm.png" alt="gatsby-new-theme" width="800" height="1110"&gt;&lt;/a&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hij95knr5p9ckx8epgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hij95knr5p9ckx8epgd.png" alt="gatsby-site-writing-markets" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmnreia1zmpe0g1aiubl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmnreia1zmpe0g1aiubl.png" alt="claude-web-scraper-plan" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will probably see if it will refactor this with &lt;a href="https://scrapy.org/" rel="noopener noreferrer"&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&gt;continue&lt;/code&gt; 4 or 5 times and here is the final result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2F2025%2Fclaude-desktop-vibe-coding-part-1%2Fclaude-web-scraper-project-structure.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2F2025%2Fclaude-desktop-vibe-coding-part-1%2Fclaude-web-scraper-project-structure.png" alt="claude-web-scraper-project-structure" width="800" height="2288"&gt;&lt;/a&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&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="https://www.docker.com/blog/the-model-context-protocol-simplifying-building-ai-apps-with-anthropic-claude-desktop-and-docker/" rel="noopener noreferrer"&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 1 and I’ll write about it as I go.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Exporting Mac OSX Book Highlights into an Obsidian Vault or Markdown Files</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Mon, 13 Jan 2025 14:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/exporting-mac-osx-book-highlights-into-an-obsidian-vault-or-markdown-files-40lg</link>
      <guid>https://forem.com/eristoddle/exporting-mac-osx-book-highlights-into-an-obsidian-vault-or-markdown-files-40lg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbh7cm4nw3ck6om4rbh0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbh7cm4nw3ck6om4rbh0.jpg" alt="Image description" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Readwise is a great idea, but probably more useful for someone who highlights and takes notes on more platforms. I mainly highlight ebooks and the whole reason I used Readwise was to get those highlights and notes into Obsidian. I take notes on the web, but the &lt;a href="https://dev.to/eristoddle/obsidians-new-web-clipper-youll-want-to-try-it-4ifp"&gt;Obsidian Web Clipper&lt;/a&gt; and send highlights directly to Obsidian, even on my iPad, once I discovered the &lt;a href="https://kagi.com/orion/" rel="noopener noreferrer"&gt;Orion Web Browser&lt;/a&gt;, which allowed me to install the Chrome plugin.&lt;/p&gt;

&lt;p&gt;Which I why I went hunting for a way to import these highlights and notes without a subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Readwise to Import Highlights into Obsidian
&lt;/h2&gt;

&lt;p&gt;The only highlights that &lt;a href="https://readwise.io/" rel="noopener noreferrer"&gt;Readwise&lt;/a&gt; retrieves semi-automatically are from the books I buy from Kindle, by going into the Readwise app and clicking a button. If I upload them to Kindle or need highlights from the Apple Books app, I have to open the book, go to my highlights, select them all, and then email them to a Readwise email address.&lt;/p&gt;

&lt;p&gt;Then when I open my Obsidian vault where the &lt;a href="https://github.com/readwiseio/obsidian-readwise" rel="noopener noreferrer"&gt;Readwise plugin&lt;/a&gt; is installed, it imports all my highlights as notes. The configuration setting in the plugin are pretty simple but they do the job:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a4bz5qvgzz90b8x9kyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a4bz5qvgzz90b8x9kyc.png" alt="obsidian-readwise" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But these aren’t all the settings for the import. I didn’t realize this at first. Part of the configuration is on the Readwise site in the &lt;a href="https://readwise.io/export" rel="noopener noreferrer"&gt;export configuration page.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foenqcpkvqnwlaxtlmrol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foenqcpkvqnwlaxtlmrol.png" alt="readwise-export-integrations" width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on the &lt;a href="https://readwise.io/export/obsidian/preferences" rel="noopener noreferrer"&gt;Obsidian integration card,&lt;/a&gt; I can configure a few more things:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0gkkbum2cfnuqzk9xlk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0gkkbum2cfnuqzk9xlk1.png" alt="obsidian-readwise-export-settings" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default template for highlights created a note that looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6nb1gz7mwjm1e6ap3rh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6nb1gz7mwjm1e6ap3rh.png" alt="obsidian-default-readwise-note" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing I planned to get to but never have was customizing the template for the export by clicking on &lt;strong&gt;Use Custom Formatting&lt;/strong&gt; , because by default it doesn’t add any yaml frontmatter, which I wanted. But it seems pretty flexible:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6mqhw7fd6mipxe9fu29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6mqhw7fd6mipxe9fu29.png" alt="obsidian-readwise-export-template" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But like I said, all I used it for was ebook highlights, so I have been researching ways to do that without paying for a service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obsidian Plugin to Import Emailed Ebook Highlights
&lt;/h2&gt;

&lt;p&gt;This was my first idea, since I had to do this with most of my ebooks when I used Readwise anyway and I started creating the &lt;a href="https://github.com/eristoddle/obsidian-email-attachment-import-plugin" rel="noopener noreferrer"&gt;plugin to work with Gmail&lt;/a&gt;. It semi-works, but it is kind of clunky and a pain in the ass and I’m not sure I will go back to work on it. One reason is that working with the Gmail API is not fun. I would have to create a service to handle that if I wanted to release it for the community. Another is that I get very limited metadata when I email the highlights and it would be nice to have more.&lt;/p&gt;

&lt;p&gt;And then I found out that the OSX Books app stores all information on highlights and notes in an open, though hard to find, SQLite database. You can see how Calibre imports these highlights &lt;a href="https://github.com/davidfor/calibre-annotations/blob/master/readers/_iBooks.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I import most of the ebooks I already own into the Books app and buy most of my ebooks on Kindle, where I am hoping there is an API that Readwise uses which I can figure out how to use. That’s for another blog post though. But with this script and hopefully a Kindle API, I’ll get all of my book highlights into Obsidian.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing Apple Books Highlights into Obsidian with Python (or Just Into a Folder)
&lt;/h2&gt;

&lt;p&gt;Yes, the title says “with Python”, but &lt;strong&gt;you don’t need to know how to write code&lt;/strong&gt; , just follow a few instructions. The Python script below &lt;strong&gt;will also work standalone to export highlights in markdown format&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install the &lt;a href="https://github.com/nickrallison/obsidian-python-scripter" rel="noopener noreferrer"&gt;Obsidian Python Scripter&lt;/a&gt; plugin. You can find it by searching community plugins in Obsidian&lt;/li&gt;
&lt;li&gt;Optional: Install the Python ebooklib library. With this installed, it uses the path of the ebook that’s in the SQLite database to get even more metadata as well as the cover image, if it’s available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are unfamiliar with installing Python libraries, open the &lt;strong&gt;Terminal&lt;/strong&gt; app and type the following line to install ebooklib. It will run without ebooklib, but will provide less data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install ebooklib

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Python OSX Book Highlight Export Script
&lt;/h3&gt;

&lt;p&gt;Then download this Python script as &lt;strong&gt;osx_book_notes.py&lt;/strong&gt; from this &lt;a href="https://gist.github.com/eristoddle/5a8e7dd0597d09d00aa5de066788c303" rel="noopener noreferrer"&gt;gist&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import glob
import sqlite3
import logging
import sys
from typing import List, Tuple, NamedTuple

ANNOTATION_DB_PATTERN = "~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation*.sqlite"
LIBRARY_DB_PATTERN = (
    "~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary*.sqlite"
)
logging.basicConfig(
    level=logging.ERROR, format="%(asctime)s - %(levelname)s - %(message)s"
)

class BookDetail(NamedTuple):
    asset_id: str
    title: str
    author: str | None
    description: str | None
    epub_id: str | None
    path: str | None
    isbn: str | None
    language: str | None
    publisher: str | None
    publication_date: str | None
    rights: str | None
    subjects: list[str] | None
    cover: str | None

def sanitize_frontmatter(text: str) -&amp;gt; str:
    if not text:
        return ""

    replacements = {
        ":": " -",
        "[": "(",
        "]": ")",
        "{": "(",
        "}": ")",
        "#": "",
        "|": "-",
        "&amp;gt;": "-",
        "\\": "/",
        "\n": " ",
        "\r": " ",
    }

    result = str(text)
    for char, replacement in replacements.items():
        result = result.replace(char, replacement)

    result = " ".join(result.split())

    return result.strip()

def get_epub_metadata(epub_path: str):
    try:
        import ebooklib
        from ebooklib import epub
        import base64

        if not epub_path:
            return None

        try:
            book = epub.read_epub(epub_path)

            metadata = {
                "isbn": next(
                    (
                        val
                        for _, val in book.get_metadata("DC", "identifier")
                        if isinstance(val, str) and "isbn" in val.lower()
                    ),
                    None,
                ),
                "language": next(
                    (val[0] for val in book.get_metadata("DC", "language")), None
                ),
                "publisher": next(
                    (val[0] for val in book.get_metadata("DC", "publisher")), None
                ),
                "publication_date": next(
                    (val[0] for val in book.get_metadata("DC", "date")), None
                ),
                "rights": next(
                    (val[0] for val in book.get_metadata("DC", "rights")), None
                ),
                "subjects": [val[0] for val in book.get_metadata("DC", "subject")],
            }

            cover_base64 = None
            for item in book.get_items():
                if item.get_type() == ebooklib.ITEM_COVER:
                    cover_data = item.get_content()
                    cover_base64 = base64.b64encode(cover_data).decode("utf-8")
                    break

            metadata["cover"] = cover_base64
            return metadata

        except Exception as e:
            print(f"Error reading epub: {e}")
            return None

    except ImportError:
        return None

def get_db_path(pattern: str) -&amp;gt; str:
    paths = glob.glob(os.path.expanduser(pattern))
    if not paths:
        raise FileNotFoundError(f"No database found matching pattern: {pattern}")
    return paths[0]

def get_book_details() -&amp;gt; List[BookDetail]:
    try:
        with sqlite3.connect(get_db_path(LIBRARY_DB_PATTERN)) as conn:
            cursor = conn.cursor()
            cursor.execute(
                """SELECT ZASSETID, ZSORTTITLE, ZSORTAUTHOR, ZBOOKDESCRIPTION, ZEPUBID, ZPATH
              FROM ZBKLIBRARYASSET"""
            )
            return [
                BookDetail(
                    asset_id=row[0],
                    title=row[1],
                    author=row[2],
                    description=row[3],
                    epub_id=row[4],
                    path=row[5],
                    isbn=None,
                    language=None,
                    publisher=None,
                    publication_date=None,
                    rights=None,
                    subjects=None,
                    cover=None,
                )
                for row in cursor.fetchall()
            ]
    except sqlite3.Error as e:
        logging.error(f"Database error: {e}")
        raise

def get_books_with_highlights() -&amp;gt; List[str]:
    book_ids = [book.asset_id for book in get_book_details()]
    placeholders = ",".join("?" for _ in book_ids)
    try:
        with sqlite3.connect(get_db_path(ANNOTATION_DB_PATTERN)) as conn:
            cursor = conn.cursor()
            cursor.execute(
                f"""SELECT DISTINCT ZANNOTATIONASSETID
                              FROM ZAEANNOTATION
                              WHERE ZANNOTATIONASSETID IN ({placeholders})
                              AND ZANNOTATIONSELECTEDTEXT != "";""",
                book_ids,
            )
            return [entry[0] for entry in cursor.fetchall()]
    except sqlite3.Error as e:
        logging.error(f"Database error: {e}")
        raise

def export_annotations(
    asset_id: str, book_details: List[BookDetail], file_path: str, extra_meta: dict
) -&amp;gt; None:
    try:
        with sqlite3.connect(get_db_path(ANNOTATION_DB_PATTERN)) as conn:
            cursor = conn.cursor()
            cursor.execute(
                """SELECT ZANNOTATIONSELECTEDTEXT, ZANNOTATIONNOTE, ZANNOTATIONLOCATION
                              FROM ZAEANNOTATION
                              WHERE ZANNOTATIONASSETID = ? AND ZANNOTATIONSELECTEDTEXT != "";""",
                (asset_id,),
            )
            annotations = cursor.fetchall()
    except sqlite3.Error as e:
        logging.error(f"Database error: {e}")
        raise

    create_file(book_details, annotations, file_path, extra_meta)

def create_file(
    book_detail: BookDetail,
    annotations: List[Tuple[str, str, str]],
    file_path: str,
    extra_meta: dict,
) -&amp;gt; None:
    if extra_meta:
        book_detail = book_detail._replace(**extra_meta)
    try:
        # Frontmatter
        output_md = "---\n"
        for key, value in {
            field: getattr(book_detail, field) for field in BookDetail._fields
        }.items():
            if value and key != "cover":
                output_md += f"{key}: {sanitize_frontmatter(value)}\n"
        output_md += "---\n\n"

        # Title
        output_md += f"# {book_detail.title} by {book_detail.author}\n\n"

        # Cover image
        if extra_meta and extra_meta.get("cover"):
            output_md += f"{% responsive_image path: data:image/jpeg;base64,{extra_meta['cover']} alt: "Cover" rest: "" %}\n\n"

        # Metadata
        output_md += "## Metadata\n\n"
        for key, value in {
            field: getattr(book_detail, field) for field in BookDetail._fields
        }.items():
            if key == "path":
                output_md += f"- {key}: [{value}](file://{value})\n"
            elif value and key != "cover":
                output_md += f"- {key}: {value}\n"

        # Annotations
        output_md += "\n"
        output_md += "## Annotations\n\n"
        for highlight, note, location in annotations:
            # TODO: See if something like this can be used
            # epubcfi_link = f"epub://{book_detail.path}#{location}"
            # output_md += f"### Location: [Open in iBooks]({epubcfi_link})\n\n"
            output_md += "\n".join([f"&amp;gt; {line}" for line in highlight.split("\n")])
            output_md += f"\n\n"
            if note:
                output_md += f"{note}\n\n"
            output_md += f"---\n\n"

        file_name = f"{book_detail.title} - {book_detail.author}.md"
        with open(
            (
                os.path.abspath(os.path.join(file_path, file_name))
                if file_path
                else file_name
            ),
            "w",
        ) as mdfile:
            mdfile.write(output_md)

    except IOError as e:
        logging.error(f"Error writing to file: {e}")
        raise

def main():
    try:
        file_path = None
        if len(sys.argv) &amp;gt; 1:
            file_path = sys.argv[1]
        if len(sys.argv) &amp;gt; 3:
            vault_path = sys.argv[1]
            folder = sys.argv[3]
            file_path = os.path.join(vault_path, folder)
        book_details = get_book_details()
        books_with_highlights = get_books_with_highlights()
    except (FileNotFoundError, sqlite3.Error) as e:
        logging.error(f"Error initializing: {e}")
        print("An error occurred accessing the Books database.")
        return

    for book in books_with_highlights:
        try:
            book_detail = next((bd for bd in book_details if bd.asset_id == book), None)
            if book_detail:
                extra_meta = get_epub_metadata(book_detail.path)
                export_annotations(book, book_detail, file_path, extra_meta)
                print(f"Exported annotations for book: {book_detail.title}")
            else:
                logging.error(f"Book details not found for asset_id: {book}")
        except (ValueError, sqlite3.Error, IOError) as e:
            print(f"Error exporting annotations: {e}")

if __name__ == " __main__":
    main()

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

&lt;/div&gt;



&lt;p&gt;And add it to your Obsidian vault in the following folder (&lt;code&gt;YourVault/.obsidian/scripts/python/&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwf0nkz7a5z79icq4l28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwf0nkz7a5z79icq4l28.png" alt="obsidian-python-file-location" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.obsidian&lt;/code&gt; folder is hidden in the Mac OS. Type &lt;strong&gt;Command + Shift + . (period)&lt;/strong&gt; to show it. If the &lt;code&gt;scripts&lt;/code&gt; or &lt;code&gt;python&lt;/code&gt; folders do not exist, create them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the Obsidian Python Scripter Plugin
&lt;/h3&gt;

&lt;p&gt;Enable the Python Scripter plugin. Once you do, go to its configuration page and you should see an entry for &lt;strong&gt;osx_book_notes.py&lt;/strong&gt;. You can leave everything that is set the same if you want your book highlights imported into the root directly of your vault. If you want to specify a directory, click on the &lt;strong&gt;Add Argument&lt;/strong&gt; button, which will add the &lt;strong&gt;Arg 3&lt;/strong&gt; entry to the form. Enter the path to the folder you want your notes stored there. And that’s all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3tdo8rpvrd80psi840q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3tdo8rpvrd80psi840q.png" alt="obsidian-osx-book-notes-configuration" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the Highlight Import
&lt;/h3&gt;

&lt;p&gt;When you want to import highlights. Click &lt;strong&gt;Command + P&lt;/strong&gt; to open the command palette, search for “python” and click on &lt;strong&gt;Python Scripter: Run osx_book_notes.py&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6yiq67hmysing2kp9ht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6yiq67hmysing2kp9ht.png" alt="obsidian-run-book-notes-import" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Results
&lt;/h3&gt;

&lt;p&gt;Here’s an example note:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2Fobsidian%2Fobsidian-book-notes-export.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2Fobsidian%2Fobsidian-book-notes-export.png" alt="obsidian-book-notes-export" width="800" height="2327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future: A Full-Fledged Obsidian Plugin to Import Book Highlights
&lt;/h2&gt;

&lt;p&gt;I am actually just as familiar with JavaScript as I am with Python, but I am not as familiar with the Obsidian plugin API yet and I know how fiddly I can get with UI, features, and configuration. So I started with Python instead of figuring that and the ebook part out at the same time. With the Python script, I could prototype the interactions with the Books db and be sure everything would work. It actually worked better than expected, which surprised me. I pulled more data than Readwise did.&lt;/p&gt;

&lt;p&gt;So one day I will turn this into an actual Obsidian plugin, now that I have the ebooks side of the equation figured out. Then it can have more features and options. There is not much you can do with this other than modify the Python script to do different things.&lt;/p&gt;

&lt;p&gt;One issue with this Python implementation is that it imports all the highlights every time. This is great to keep everything in sync. Even with books you haven’t finished will be synced. The script just overwrites the old file. But the first time I ran it on my main Apple account, I realized how many accidential highlights I create. So I used my imported notes to find the books, removed the unwanted highlights in the Books app, deleted the notes, and ran the script again.&lt;/p&gt;

&lt;p&gt;But when I create a plugin to do this, I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store a value from the SQLite database in metadata to determine when the book was last opened or highlighted and skip it (I have a dataview that sorts these by the modifed date and right now every one is modified every time in no specific order)&lt;/li&gt;
&lt;li&gt;Set default yaml entries for imported notes (for example, a &lt;code&gt;reviewed&lt;/code&gt; boolean or a &lt;code&gt;#book/notes&lt;/code&gt; tag )&lt;/li&gt;
&lt;li&gt;Configuration to set a template for a note so they can be customized&lt;/li&gt;
&lt;li&gt;Autosync on open&lt;/li&gt;
&lt;li&gt;Configuration option to provide a dialog to pick and choose which books to import&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See what I meant about getting fiddly with features. Doing it this way first gave me an MVP in a few hours.&lt;/p&gt;

</description>
      <category>obsidian</category>
      <category>python</category>
    </item>
    <item>
      <title>Obsidian's New Web Clipper - You'll Want to Try It</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Wed, 27 Nov 2024 12:00:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/obsidians-new-web-clipper-youll-want-to-try-it-4ifp</link>
      <guid>https://forem.com/eristoddle/obsidians-new-web-clipper-youll-want-to-try-it-4ifp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdnhpnzq0r0aof2v93w7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdnhpnzq0r0aof2v93w7.png" alt="Obsidian web clipper" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few months ago, I “settled” on the process I’d used to collect information from the web when I wrote my post about &lt;a href="https://dev.to/eristoddle/how-i-finally-ditched-evernote-for-joplin-2kjb"&gt;migrating from Evernote to Joplin&lt;/a&gt;. Because I wanted to keep my Obsidian vault relatively clean, I decided I’d use Joplin’s web clipper to save any information I found online and then migrate my notes to Obsidian when I was sure I needed them there.&lt;/p&gt;

&lt;p&gt;But the new &lt;a href="https://obsidian.md/clipper" rel="noopener noreferrer"&gt;Obsidian Web Clipper&lt;/a&gt; has me rethinking things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;The Original Obsidian Clipper&lt;/li&gt;
&lt;li&gt;
The New Obsidian Web Clipper

&lt;ul&gt;
&lt;li&gt;Saving Web Clips to an Obsidian Vault&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Other Obsidian Web Clipper General Settings&lt;/li&gt;

&lt;li&gt;Using the Web Clipper Highlighter&lt;/li&gt;

&lt;li&gt;Using Templates with the Web Clipper&lt;/li&gt;

&lt;li&gt;An Obsidian Web Clipper Custom Template and Variable Example&lt;/li&gt;

&lt;li&gt;Adding the Obsidian Web Clipper to My Workflow&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Original Obsidian Clipper
&lt;/h2&gt;

&lt;p&gt;There has been a community &lt;a href="https://github.com/jplattel/obsidian-clipper" rel="noopener noreferrer"&gt;clipper&lt;/a&gt; around for a while. And it did have some useful features. You just set all these options and it would save a web page to the vault, folder, and file you told it to. You could even use a template for content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe761pcj1lrf0oa86xzxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe761pcj1lrf0oa86xzxq.png" alt="original-obsidian-web-clipper-options" width="800" height="859"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And at the time I used Evernote and when I wanted to save a web page, I just saved the whole thing to a Collect folder, so this Obsidian web clipper worked the same way I used a web clipper at the time. But I stopped using this Obsidian web clipper because I didn’t want to have a folder full of hundreds of random files I collected because they might be interesting for something along with those I might use tomorrow.&lt;/p&gt;

&lt;p&gt;But the new official Obsidian Web Clipper has me backtracking and rethinking how I collect information from the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Obsidian Web Clipper
&lt;/h2&gt;

&lt;p&gt;You can find links to Obsidian Web Clipper browser extensions &lt;a href="https://obsidian.md/clipper#more-browsers" rel="noopener noreferrer"&gt;here&lt;/a&gt;, for Chrome, Safari, Firefox, Edge, Brave, Arc, Orion, and Vivaldi. And here’s what it looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn8oybtweodgby4khn6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn8oybtweodgby4khn6s.png" alt="obsidian-chrome-web-clipper" width="800" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See all the frontmatter data it generates from the page, including the title, source, author, and more. I can see this being useful some time, but it wasn’t the feature that made me change my mind, though it got me partially there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving Web Clips to an Obsidian Vault
&lt;/h3&gt;

&lt;p&gt;Both web clippers require you to manually type in vault and folder names. I know Joplin runs a service for web clipping which allows the browser extension to interact directly with Joplin to add a folder choice dropdown, tags, etc. This will not happen with the Obsidian Web Clipper. You will have to set these values.&lt;/p&gt;

&lt;p&gt;So in my screenshot example, I had set up the &lt;strong&gt;Writing&lt;/strong&gt; vault in settings, so it was available. You can get to settings by clicking on the gear in the extension’s modal. Then I just added the name of my vault here in the &lt;strong&gt;General&lt;/strong&gt; settings section:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w648citwo1ampsihs8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w648citwo1ampsihs8h.png" alt="add-obsidian-web-clipper-vault" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The field right beside the vault in my screenshot is the folder you want to use. You can set also set this in settings, which we’ll get to in the templates section, but I didn’t know this at first and ended up typing it out each time. Then I noticed this feature after I clicked on the caret at the end of the &lt;strong&gt;Add to Obsidian&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8jkvuj4fdj2iu2tuvcx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8jkvuj4fdj2iu2tuvcx.png" alt="save-obsidian-web-clip-to-file" width="724" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is more than one way to add to Obsidian and browsing to the folder I want to store the clip might just be simpler and not take much longer than clicking the button. It definitely is easier that typing the folder path out again.&lt;/p&gt;

&lt;p&gt;You can also use &lt;strong&gt;Copy to clipboard&lt;/strong&gt; and paste the whole contents of the file where needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Obsidian Web Clipper General Settings
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;General&lt;/strong&gt; settings, you can also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose to open the note in Obsidian after it is clipped or not&lt;/li&gt;
&lt;li&gt;Downgrade the clipper for Obsidian installations that are version 1.6.7 or older&lt;/li&gt;
&lt;li&gt;Reset the Default template back to default&lt;/li&gt;
&lt;li&gt;Export web clipper settings&lt;/li&gt;
&lt;li&gt;Import web clipper settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using the Web Clipper Highlighter
&lt;/h2&gt;

&lt;p&gt;This was the part of the web clipper that had me rethinking collecting notes online. Yes, there are other tools that will do this, but then I would have to import the results into Obsidian. To enable the highlighter, click the highlighter button in the heading of the web clipper.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl32nt5qxm6skyd0anz3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl32nt5qxm6skyd0anz3c.png" alt="enable-obsidian-highlighter" width="700" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will hide the modal and bring up highlighter mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmu1weufvn9uq0u9ryxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmu1weufvn9uq0u9ryxu.png" alt="obsidian-clip-highlights" width="800" height="886"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can click and drag to highlight a specific phrase, sentence, or sentences. Or you can hover over a paragraph, like I do at the bottom of the screenshot, and click once to highlight the whole paragraph. At the top of the window, it keeps a count of your highlights and you can click the trashcan to delete them.&lt;/p&gt;

&lt;p&gt;When you are done highlighting, just click &lt;strong&gt;Clip highlights&lt;/strong&gt; and then &lt;strong&gt;Add to Obsidian&lt;/strong&gt;. And depending on how you have the &lt;strong&gt;Highlighter&lt;/strong&gt; settings set up (get to it from the gear button in the clipper), it will either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save just your highlights (what I am doing)&lt;/li&gt;
&lt;li&gt;Save the content with the highlights&lt;/li&gt;
&lt;li&gt;Save the content without highlights&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also set the extension to &lt;strong&gt;Always show highlights&lt;/strong&gt; if you want and they will be visible when you come back to the page. You can also export all your highlights from the settings page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Templates with the Web Clipper
&lt;/h2&gt;

&lt;p&gt;Obsidian Web Clipper comes with a default template that works pretty well for my use case. You can edit this template or create new ones and then choose the one you want to use from the clipper’s heading:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7jr8tvtdn7x6lvfeowx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7jr8tvtdn7x6lvfeowx.png" alt="obsidian-web-clipper-choose-template" width="720" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But there is a lot to templates and I can see me creating specific templates for specific types of information I’m collecting. You can do that in settings by clicking on the gear. Here are basic configuration values for a template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template name:&lt;/strong&gt; Self- explanatory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; You can have it 

&lt;ul&gt;
&lt;li&gt;Create a new note&lt;/li&gt;
&lt;li&gt;Add to the bottom of an existing note&lt;/li&gt;
&lt;li&gt;Add to the top of an existing note&lt;/li&gt;
&lt;li&gt;Add to the top of a daily note&lt;/li&gt;
&lt;li&gt;Add to the bottom of a daily note&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Note name:&lt;/strong&gt; By default this is set to the &lt;code&gt;title&lt;/code&gt; variable and you can modify it by adding other variables.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Note location:&lt;/strong&gt; The folder in the vault you want to hold the note. This is where the default “Clippings” folder comes from in the clipper UI. I guess I should have set it there. Well, now I know.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Vault:&lt;/strong&gt; Set which vault it will save to 

&lt;ul&gt;
&lt;li&gt;Last used&lt;/li&gt;
&lt;li&gt;[One of the vaults you added in &lt;strong&gt;General&lt;/strong&gt; settings]&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Template triggers:&lt;/strong&gt; Trigger the use of this template based on a URL pattern or the schema.org data the page contains.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Properties:&lt;/strong&gt; Variables from the page that will be stored in the yaml frontmatter of the markdown file. This is what adds author, title, source, and more.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Note content:&lt;/strong&gt; This becomes the markdown in the markdown file. By default this is set to the &lt;code&gt;content&lt;/code&gt; variable and you can modify it by adding other variables.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Speaking of variables, here are some of the variables that are automatically generated by the extension, courtesy of the &lt;a href="https://help.obsidian.md/web-clipper/variables" rel="noopener noreferrer"&gt;Obsidian docs&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F203lq14lzrkzqsglhl2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F203lq14lzrkzqsglhl2a.png" alt="available-obsidian-variables" width="800" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But those aren’t the only variables available. By clicking on the three dots menu in the clipper heading, you can find others.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sh631z6m0zb3m415y1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sh631z6m0zb3m415y1h.png" alt="obsidian-variables-menu" width="800" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more details, check out the &lt;a href="https://help.obsidian.md/web-clipper/variables" rel="noopener noreferrer"&gt;Obsidian docs on web clipper variables&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Obsidian Web Clipper Custom Template and Variable Example
&lt;/h2&gt;

&lt;p&gt;I didn’t realize the power of templates and variables until I started writing this post, but if you collect the same type of notes all the time, you can collect those notes with just the data you need and put them in just the folder you want every time, like a one-page scraper. At first, I wasn’t sure why &lt;strong&gt;Note location&lt;/strong&gt; was stored in the template, but now it makes sense.&lt;/p&gt;

&lt;p&gt;Let’s say you’re collecting movie details from &lt;a href="https://www.imdb.com/" rel="noopener noreferrer"&gt;IMDB&lt;/a&gt;. Start by going into settings and creating a new IMDB template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfww3xb0u8xgs2b9tvnb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfww3xb0u8xgs2b9tvnb.png" alt="obsidian-imdb-template" width="800" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This won’t start out as much. In fact, all the variables set up for the default template will be reset. You will still get the &lt;code&gt;title&lt;/code&gt; as the page name and get the &lt;code&gt;content&lt;/code&gt; in the note content, but that’s about it, but now you can customize it to just what you need.&lt;/p&gt;

&lt;p&gt;One thing I want to do is store all my movie notes in the same folder. So I am going to change the &lt;strong&gt;Note location&lt;/strong&gt; to “Movie Clips.”&lt;/p&gt;

&lt;p&gt;I also want this template to be used for all IMDB entries, so I am going to add &lt;code&gt;/^https:\/\/www\.imdb\.com\/title\/tt\d+\/reference\/?$/&lt;/code&gt; as a &lt;strong&gt;Template trigger&lt;/strong&gt;. If you want more than one trigger, you can add one per line in the form field, but one will work for this.&lt;/p&gt;

&lt;p&gt;After adding that, to test the template will be triggered, I went to IMDB and opened the clipper to see it selected the IMDB template:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6430y7dtoyunm7kq9xr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6430y7dtoyunm7kq9xr.png" alt="obsidian-imdb-template-trigger" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To customize the content I’m collecting, the easiest way is to click on the three dots menu in the header to get to the &lt;strong&gt;Page variables&lt;/strong&gt; dialog. I know I want the title of the movie, so I click on the purple title, which copies the variable to the clipboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9059odiw5xxgz765oo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9059odiw5xxgz765oo2.png" alt="use-obsidian-page-variables" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then go back to the template I am creating in settings and click &lt;strong&gt;Add property&lt;/strong&gt; under &lt;strong&gt;Properties&lt;/strong&gt;. In the property name field on the left side, I name the variable and then I past the value I copied from the page variables into the property value field on the right side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8up16ockx9dovkg647hq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8up16ockx9dovkg647hq.png" alt="adding-obsidian-web-clipper-properties" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will add the title to the yaml frontmatter in the file, so you can use it in plugins like Data View.&lt;/p&gt;

&lt;p&gt;But maybe I don’t want to just dump the whole content of the page into the content of my note and would rather have it more structured. I can remove the default &lt;code&gt;content&lt;/code&gt; variable in the &lt;strong&gt;Note content&lt;/strong&gt; field and add specific variables there.&lt;/p&gt;

&lt;p&gt;This all happens as soon as you change it, so you can add something to your template, then go back to the clipper to see what is will clip until you get want you want. Here’s what I ended up adding, just playing around to see what would work:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F043902mcjfe9m8c9wua9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F043902mcjfe9m8c9wua9.png" alt="obsidian-custom-template-variables" width="800" height="944"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is the result of the note in two screenshots because the image was huge. You can see the properties here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsbfqie80dvmwjtxkcus.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsbfqie80dvmwjtxkcus.jpeg" alt="obsidian-clipped-note-properties" width="800" height="878"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is the result of variable I set up for the body of the note:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvav4uo4k4hoozbfn7703.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvav4uo4k4hoozbfn7703.png" alt="obsidian-note-conent-variables" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Obsidian Web Clipper to My Workflow
&lt;/h2&gt;

&lt;p&gt;I still have the tendency to collect random things online that have nothing to do with my current projects, so I will still be using Joplin as my “internet junk drawer” and at least half the stuff I find will eventually get deleted.&lt;/p&gt;

&lt;p&gt;But, sometimes I am doing research for articles I know I am going to write, I have time to read what I am looking at, and want to only collect specific details from the page. That would be my perfect use case for the Obsidian web clipper, though I might find more. The fact that it stores author, url, and title will also be very useful.&lt;/p&gt;

</description>
      <category>obsidian</category>
    </item>
    <item>
      <title>How to Install, Activate, and Update Obsidian Plugins</title>
      <dc:creator>Stephan Miller</dc:creator>
      <pubDate>Wed, 30 Oct 2024 06:06:00 +0000</pubDate>
      <link>https://forem.com/eristoddle/how-to-install-activate-and-update-obsidian-plugins-4d2p</link>
      <guid>https://forem.com/eristoddle/how-to-install-activate-and-update-obsidian-plugins-4d2p</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo37uxumplcv40ity8cjr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo37uxumplcv40ity8cjr.jpg" alt="Image description" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The biggest thing I don’t like about Obsidian is that it’s closed source, but the fact that you find or write a plugin to do just about anything you want mitigates that a lot. This article won’t cover writing a plugin, just using existing plugins, but I will get to writing that post eventually, because despite the hundreds of plugins available, I’ve had a few ideas for new ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Where to Install Obsidian Plugins&lt;/li&gt;
&lt;li&gt;Core Obsidian Plugins&lt;/li&gt;
&lt;li&gt;How to Install Obsidian Community Plugins&lt;/li&gt;
&lt;li&gt;Popular Community Obsidian Plugins&lt;/li&gt;
&lt;li&gt;How to Update Obsidian Plugins&lt;/li&gt;
&lt;li&gt;How to Install Beta Obsidian Plugins with BRAT&lt;/li&gt;
&lt;li&gt;How to Install Obsidian Plugins Manually&lt;/li&gt;
&lt;li&gt;How to Copy Plugins to a New Vault&lt;/li&gt;
&lt;li&gt;Obsidian Plugins that Work on Some Platforms But Not Others&lt;/li&gt;
&lt;li&gt;Don’t Forget to Activate and Configure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to Install Obsidian Plugins
&lt;/h2&gt;

&lt;p&gt;You can skip this section if you already know where to install plugins in Obsidian, but I put it here for newbies. This brings up the irony of cross-platform apps. An app may be cross-platform, but that doesn’t mean it will act the same (in general, Obsidian does) or have the same UI (Obsidian doesn’t) across every platform.&lt;/p&gt;

&lt;p&gt;Now that I think of it, the fact that &lt;strong&gt;you add plugins to a vault and not to the Obsidian application&lt;/strong&gt; sort of threw me. It makes sense now that I have a better understanding of how Obsidian is structured. But initially, when I created my second vault, I wondered where all my plugins were.&lt;/p&gt;

&lt;p&gt;Which means you might install the perfect set of plugins in one vault and you’ll just have to repeat the entire process in the next one you create. But in a section below, I’ll show you how to copy plugins to a new vault quickly.&lt;/p&gt;

&lt;p&gt;Here is, in general, where you can reach the Obsidian &lt;strong&gt;Settings&lt;/strong&gt; menu, which is where you manage plugins.&lt;/p&gt;

&lt;p&gt;In the desktop app, you can get to &lt;strong&gt;Settings&lt;/strong&gt; from the gear at the bottom of the side menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fll71fpxcuik98i0d66n7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fll71fpxcuik98i0d66n7.png" alt="obsidian-settings-menu-desktop" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or by going here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F51qxv2jnb57ze4x24ui5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F51qxv2jnb57ze4x24ui5.png" alt="obsidian-settings-from-menu" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the mobile app, you find the &lt;strong&gt;Settings&lt;/strong&gt; menu by clicking the gear at the top of the side menu/drawer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ieleh7bmjr90h3u0rwl.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ieleh7bmjr90h3u0rwl.jpeg" alt="obisidian-settings-menu-mobile" width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Obsidian Plugins
&lt;/h2&gt;

&lt;p&gt;Did I ever tell you I like software with plugins? Well, I really like software where most of the core functionality is in plugins. I thought there was a term for this, like “plugin-first development”, but couldn’t find one. If there isn’t, there should be.&lt;/p&gt;

&lt;p&gt;But it is nice to see when an application has “core” plugins. This usually means that even if the application isn’t open source, you get access too much of the same functionality that “core” developers do. You just have to write a plugin.&lt;/p&gt;

&lt;p&gt;Here is a list of core Obsidian plugins. Not all are enabled by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Enabled By Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Audio recorder&lt;/td&gt;
&lt;td&gt;Lets you record and save voice recordings in a note.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backlinks&lt;/td&gt;
&lt;td&gt;Shows you all the backlinks for the current note.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bookmarks&lt;/td&gt;
&lt;td&gt;Allows you to bookmark various things in your vault for quicker access.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command palette&lt;/td&gt;
&lt;td&gt;Gives you quick access to commands from your keyboard.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily notes&lt;/td&gt;
&lt;td&gt;Create and open notes based on the current date.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File explorer&lt;/td&gt;
&lt;td&gt;This is what shows up in the left side of Obsidian and lists all the files.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File recovery&lt;/td&gt;
&lt;td&gt;Keeps snapshots of your notes so you can recover them if needed.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format converter&lt;/td&gt;
&lt;td&gt;Converts markdown from other apps to a format Obsidian can use.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graph view&lt;/td&gt;
&lt;td&gt;Let’s you visualize the relationships between your notes.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Importer&lt;/td&gt;
&lt;td&gt;Converts files from various apps to Obsidian format.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Note composer&lt;/td&gt;
&lt;td&gt;Allows you to merge or split notes.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outgoing links&lt;/td&gt;
&lt;td&gt;Shows all outgoing links for the currently active note.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outline&lt;/td&gt;
&lt;td&gt;Shows a table of contents for the current note.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page preview&lt;/td&gt;
&lt;td&gt;Preview a note by hovering over a link to it.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Properties view&lt;/td&gt;
&lt;td&gt;List all the properties in your vault and see the properties for the current note.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publish&lt;/td&gt;
&lt;td&gt;Allows you to publish your notes if you have signed up for &lt;a href="https://obsidian.md/publish" rel="noopener noreferrer"&gt;Obsidian Publish&lt;/a&gt;.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick switcher&lt;/td&gt;
&lt;td&gt;Allows you to search, create, and open notes with your keyboard.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random note&lt;/td&gt;
&lt;td&gt;Opens a random note in your vault.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Find files in your vault.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slash commands&lt;/td&gt;
&lt;td&gt;Perform commands inside the editor using the &lt;code&gt;/&lt;/code&gt; key. Similar to the way Slack apps work.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slides&lt;/td&gt;
&lt;td&gt;Create a presentation from your notes.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;Syncs your vault if you’ve signed up for &lt;a href="https://obsidian.md/sync" rel="noopener noreferrer"&gt;Obsidian Sync&lt;/a&gt;.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tags view&lt;/td&gt;
&lt;td&gt;List all the tags in your vault.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Templates&lt;/td&gt;
&lt;td&gt;Create notes with pre-defined templates&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unique note creator&lt;/td&gt;
&lt;td&gt;Creates a unique note using a time-coded title.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Word count&lt;/td&gt;
&lt;td&gt;Displays the number of words and characters for the active note.&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workspaces&lt;/td&gt;
&lt;td&gt;Save your layouts and switch between them.&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At first I thought I’d go over all the details in each of these plugins in this article, but that would probably make this section 75% of the article. So, in this article, I’ll just going over the basics.&lt;/p&gt;

&lt;p&gt;You can get to the list of core plugins by going to the &lt;strong&gt;Options&lt;/strong&gt; section of &lt;strong&gt;Settings&lt;/strong&gt; and click on the &lt;strong&gt;Core plugins&lt;/strong&gt; menu item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo852msxk4ue5zeh13t0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo852msxk4ue5zeh13t0j.png" alt="obisidian-core-plugins" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this view, you can enable and disable the plugin with the toggle. The gear in the plugin row will take you to its configuration. Only some have this. You can also reach a plugin’s configuration by clicking on the name of the specific core plugin in the &lt;strong&gt;Core plugin&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84bgz8i8udb6ubrpjygq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84bgz8i8udb6ubrpjygq.png" alt="obisidian-core-plugin-configuration" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The plus (+) in the plugin row will take you to dialog for editing the plugins hotkeys (aka keyboard shortcuts). Some come with built-in hotkeys which you can edit. Some don’t, but you can add custom hotkeys to each of the plugin’s commands.&lt;/p&gt;

&lt;p&gt;You can also go directly to the hotkeys by clicking &lt;strong&gt;Hotkeys&lt;/strong&gt; in the &lt;strong&gt;Options&lt;/strong&gt; section and search for a specific one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13wr49f7l9we7nlhjwml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13wr49f7l9we7nlhjwml.png" alt="obsidian-hotkeys" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Install Obsidian Community Plugins
&lt;/h2&gt;

&lt;p&gt;In order to install community plugins, first, you have to enable the ability to install them. Go to the &lt;strong&gt;Options&lt;/strong&gt; section of &lt;strong&gt;Settings&lt;/strong&gt; and click on the &lt;strong&gt;Community plugins&lt;/strong&gt; menu item. Then click the &lt;strong&gt;Turn on community plugins&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9538pomib8t53dvcw1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9538pomib8t53dvcw1x.png" alt="activate-obsidian-community-plugins" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will bring you to this screen. Click on the &lt;strong&gt;Browse&lt;/strong&gt; button to search for plugins.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8q0o1ewqydgkdpxphdpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8q0o1ewqydgkdpxphdpw.png" alt="browse-obsidian-community-plugins" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which will take you to this screen, where you can search for plugins or actually browse, if you want, but there are currently 1,964 plugins available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwn72qe2t9i8anowjvp3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwn72qe2t9i8anowjvp3h.png" alt="search-obsidian-community-plugins" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you find the plugin you want to install, click on its card, which will load documentation on the plugin, and a set of buttons. Click &lt;strong&gt;Install&lt;/strong&gt; to install the plugin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvf8vrk0xaa0g24qutxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvf8vrk0xaa0g24qutxl.png" alt="install-plugin-in-obsidian" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This just installs the plugin. Once it is installed, you can enable the plugin on the same page by clicking the &lt;strong&gt;Enable&lt;/strong&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1zhh4hytwo5sb7vteif.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1zhh4hytwo5sb7vteif.png" alt="enable-obsidian-plugin" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or you can enable it in the &lt;strong&gt;Community plugins&lt;/strong&gt; list by clicking on the toggle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35gotjecrdyd9wbl7ck8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35gotjecrdyd9wbl7ck8.png" alt="enable-obsidian-plugin-in-community-plugin-list" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Popular Community Obsidian Plugins
&lt;/h2&gt;

&lt;p&gt;This is a hard list to create, because there are close to 2,000 plugins and probably hundreds of use cases of Obsidian and the plugins that are “top” for you depend on your use case. But there have been a few that seem to show up on most online favorite lists and have a huge number of installs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/zsviczian/obsidian-excalidraw-plugin" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;a href="https://excalidraw.com/" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt; drawings inside of Obsidian.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/blacksmithgu/obsidian-dataview" rel="noopener noreferrer"&gt;Dataview&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Allows you to create a table of data from the markdown files in your vault. I have a view that lists all blog posts where &lt;code&gt;published&lt;/code&gt; in the frontmatter equals false to find my drafts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/tgrosinger/advanced-tables-obsidian" rel="noopener noreferrer"&gt;Advanced Tables&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Give you a toolbar that does advanced things with tables. If you ever tried creating tables in markdown manually, you’ll know why this helps.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/SilentVoid13/Templater" rel="noopener noreferrer"&gt;Templater&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;This one I haven’t dug into, but I have installed it because I want to. “It defines a templating language that lets you insert variables and functions results into your notes.”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/obsidian-tasks-group/obsidian-tasks" rel="noopener noreferrer"&gt;Tasks&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tracks tasks across your entire vault. I haven’t installed this, but it’s popular.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/liamcain/obsidian-calendar-plugin" rel="noopener noreferrer"&gt;Calendar&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Lets you visualize and navigate through your daily notes. I will probably use this. I just have to get into the practice of writing daily notes digitally instead of on paper.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mgmeyers/obsidian-kanban" rel="noopener noreferrer"&gt;Kanban&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;This plugin turns a markdown file with headings and task lists into a kanban and I use it all the time. Also why I don’t need the Tasks plugin.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Vinzent03/obsidian-git" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;This is one of the &lt;a href="https://dev.to/eristoddle/how-to-sync-obsidian-across-all-your-devices-including-free-methods-1mi5"&gt;many ways to sync your Obsidian vault&lt;/a&gt;, but I just use the &lt;a href="https://www.stephanmiller.com/sync-obsidian-vault-across-devices/#:~:text=whenever%20I%20want.-,Git,-Use%20this%20method" rel="noopener noreferrer"&gt;Git plugin&lt;/a&gt; to keep backups and check diffs between my current vault and backups.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/FlorianWoelki/obsidian-iconize" rel="noopener noreferrer"&gt;Iconize&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Allows you to add custom icons to the files and folders in your vault so its easier to find what you need in the tree view.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/vslinko/obsidian-outliner" rel="noopener noreferrer"&gt;Outliner&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Allows you to drag and drop list items like &lt;a href="https://workflowy.com/" rel="noopener noreferrer"&gt;Workflowy&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to Update Obsidian Plugins
&lt;/h2&gt;

&lt;p&gt;Updating plugins is pretty simple and, depending on how many you have to install, something you have to do pretty often. I have to at least once a week. Obsidian and core plugins will update together when there is an Obsidian update, but community plugins have to be updated separately.&lt;/p&gt;

&lt;p&gt;To check for updates, go to &lt;strong&gt;Settings&lt;/strong&gt; and then &lt;strong&gt;Community Plugins&lt;/strong&gt; and click the &lt;strong&gt;Check for updates&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo31crx32p5ze7rxyfmx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo31crx32p5ze7rxyfmx.png" alt="check-update-obsidian-community-plugins" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will run for a few seconds, notify you will a snackbar with the count of plugins that can be updated, and then the button will change to &lt;strong&gt;Update all&lt;/strong&gt;. Clicking this will do what it says.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ynedfqd1eijywkch0i7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ynedfqd1eijywkch0i7.png" alt="update-all-obsidian-community-plugins" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also update plugins one at a time, by scrolling down in the dialog and clicking the &lt;strong&gt;Update&lt;/strong&gt; button in each plugin row.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcal57hkmx0eyow8pd2qt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcal57hkmx0eyow8pd2qt.png" alt="update-single-obsidian-plugin" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t want to do this weekly, you can also install the “&lt;a href="https://github.com/swar8080/obsidian-plugin-update-tracker" rel="noopener noreferrer"&gt;Plugin Update Tracker&lt;/a&gt;” plugin which will do the checking for you automatically, along with some other features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx3qtsckpgkhhyxelpg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx3qtsckpgkhhyxelpg6.png" alt="obisidian-plugin-update-tracker" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, if you are testing a beta plugin, you will have to update and install using the BRAT plugin, which the next section covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Install Beta Obsidian Plugins with BRAT
&lt;/h2&gt;

&lt;p&gt;Many plugins that are still in development might require you to install the &lt;a href="https://github.com/TfTHacker/obsidian42-brat" rel="noopener noreferrer"&gt;BRAT plugin&lt;/a&gt;. At first I didn’t know what this was and just skipped plugins that required it, but now that I know what its for, I give them the benefit of the doubt.&lt;/p&gt;

&lt;p&gt;The BRAT plugin is for beta Obsidian plugins and themes. It stands for “Beta Reviewers Update Tool.” It allows you to install plugins that are still in development, but not yet in the list of community plugins. A good way to &lt;a href="https://github.com/obsidianmd/obsidian-releases/pulls?q=is%3Apr+is%3Aopen+%22Add+plugin%3A%22" rel="noopener noreferrer"&gt;find beta Obisidian plugins&lt;/a&gt; is looking for “Add plugin” in the pull request list for &lt;code&gt;obsidian-releases&lt;/code&gt;. Another place is the &lt;a href="https://forum.obsidian.md/c/share-showcase/9" rel="noopener noreferrer"&gt;Share &amp;amp; showcase Obsidian forum&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how you use it.&lt;/p&gt;

&lt;p&gt;First, install the plugin by searching for “BRAT” in &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Community plugins&lt;/strong&gt; &amp;gt; &lt;strong&gt;Browse&lt;/strong&gt; and make sure to enable it. The instructions for installing community plugins above walks you through the complete process.&lt;/p&gt;

&lt;p&gt;Here is its configuration page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvx9vtn2aq8iuky1kxuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvx9vtn2aq8iuky1kxuj.png" alt="configure-obsidian-brat-plugin" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To install a beta plugin, first find its Github repo. For this example, I am going to install the &lt;a href="https://github.com/zakhij/obsidian-activity-heatmap" rel="noopener noreferrer"&gt;Obsidian Activity Heatmap Plugin&lt;/a&gt;. So I am going to copy the Github url. There are two ways to do this. The easiest is just to go to the Github repo and copy the url in the browser. You can also click the Code button, choose HTTPS, and click the copy button, but you will have to remove &lt;code&gt;.git&lt;/code&gt; from the end of that url.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcxt6ttjq1ql71plccc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcxt6ttjq1ql71plccc0.png" alt="copy-obsidian-plugin-git-url-for-brat" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now go back to the BRAT configuration in Obsidian, and click on the &lt;strong&gt;Add Beta plugin&lt;/strong&gt; button. You can also click on the &lt;strong&gt;Add Beta plugin with frozen version&lt;/strong&gt; button if you don’t want to auto-update the plugin when the repo updates.&lt;/p&gt;

&lt;p&gt;Then paste the url you just copied into the form that pops up (remove .git on the end if it is there) and click &lt;strong&gt;Add Plugin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5eoozib4jng2beqcf55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5eoozib4jng2beqcf55.png" alt="paste-github-url-into-brat" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you do, you will see in listed in the BRAT plugin as well as in the &lt;strong&gt;Community plugins&lt;/strong&gt; list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93r6omx8skita2ybi70z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93r6omx8skita2ybi70z.png" alt="obsidian-plugin-installed-with-brat" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information, check out this &lt;a href="https://tfthacker.com/BRAT" rel="noopener noreferrer"&gt;documentation on the BRAT plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Install Obsidian Plugins Manually
&lt;/h2&gt;

&lt;p&gt;If you don’t want to install beta Obsidian plugins with BRAT, you can also install them manually and it’s relatively simple, but not “just click a few buttons” or “paste a url” simple. These are desktop instructions. If you want to try in mobile, it should work similarly, but who would want to on a tiny screen?&lt;/p&gt;

&lt;p&gt;I will use the &lt;a href="https://github.com/gasparschott/obsidian-continuous-mode" rel="noopener noreferrer"&gt;Obsidian Continuous Mode&lt;/a&gt; plugin as an example. This can be installed through the regular process, but here is how you do it manually. Go to the git repo where your plugin is located and download the whole thing as a zip file. See the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsd72se1lvl78sknysd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsd72se1lvl78sknysd6.png" alt="obisidian-download-plugin" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let the zip file hang out in your download folder for now. Next, find your vault on your file system and open the folder. Inside, you should find a &lt;code&gt;.obsidian&lt;/code&gt; folder. If you don’t see it, make sure that you are showing hidden folders, because this is one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh9i1tz7473i6xusfmbl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh9i1tz7473i6xusfmbl.png" alt="obisidian-folder-in-vault" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside this folder, you’ll find a &lt;code&gt;plugins&lt;/code&gt; folder if you have already installed some. If not, create it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdje0h8nevr0f9jjscqpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdje0h8nevr0f9jjscqpu.png" alt="obsidian-plugins-folder" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go back to the zip file you downloaded and extract it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgpazeo97z24tixkpo6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgpazeo97z24tixkpo6l.png" alt="extract-obisidian-plugin-zip" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, drag the folder that was extracted to the plugins folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u6614ozuvoz04xyyhfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u6614ozuvoz04xyyhfz.png" alt="drag-plugin-to-obsidian-plugin-folder" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, open the vault in Obsidian and you should see the plugin waiting to be activated in the &lt;strong&gt;Community Plugins&lt;/strong&gt; section of &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcciitrek9h13w92ahwjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcciitrek9h13w92ahwjh.png" alt="manually-installed-obsidian-plugin" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Copy Plugins to a New Vault
&lt;/h2&gt;

&lt;p&gt;So let’s say you have one vault that you’ve used for a while and you’ve installed like 50 plugins. Now you want to create a new vault and add the same plugins. Well, you can search for a plugin, click install, and repeat the process 50 times, or you can take the manual install method above a step further.&lt;/p&gt;

&lt;p&gt;Simply open the vault that has the plugins you want to use in your file system, find the &lt;code&gt;.obsidian&lt;/code&gt; folder, then the &lt;code&gt;plugins&lt;/code&gt; folder and copy the whole &lt;code&gt;plugins&lt;/code&gt; folder to the &lt;code&gt;.obsidian&lt;/code&gt; folder of your new vault.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obsidian Plugins that Work on Some Platforms But Not Others
&lt;/h2&gt;

&lt;p&gt;You will run into this. Most of the time, it will be desktop plugins that won’t work on mobile. A good example is the Pandoc plugin, which allows you to export your notes as a PDF, ePub, or DOCX on your desktop if you have Pandoc installed.&lt;/p&gt;

&lt;p&gt;On your desktop computer, it is relatively easy to install Pandoc, then the plugin, and have it work. But mobile is not the same. I went searching for how to install Pandoc on mobile devices and did not want to climb down that rabbit hole. And even if I accomplished that, setting up access to it from Obsidian is probably not straightforward either, if it’s possible.&lt;/p&gt;

&lt;p&gt;So here is the Pandoc plugin on my iPad. I can click the activate toggle all day and it does nothing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2Fobsidian%2Fobsidian-pandoc-on-mobile.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.stephanmiller.com%2Fimages%2Fobsidian%2Fobsidian-pandoc-on-mobile.PNG" alt="obsidian-pandoc-on-mobile" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what can you do? There are a few other reasons a plugin will work on one platform and not another, and usually the plugin documentation will tell you why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t Forget to Activate and Configure
&lt;/h2&gt;

&lt;p&gt;I write this section because I always forget to activate the plugin. I find something new, click install, and want to see what it does. If you are installing a beta Obsidian plugin with BRAT, you can check the box to enable it after update. But I don’t know how many times I went closed the settings menu to see new features and saw nothing. So back to settings to enable the plugin.&lt;/p&gt;

&lt;p&gt;It also doesn’t hurt to look at a plugin’s configuration (under the gear button in the plugin row) to see if you want to adjust anything there. I rarely do, but it doesn’t hurt to check.&lt;/p&gt;

&lt;p&gt;And that’s all for now. This started as a simple “how to install Obsidian plugins” post, but grew to much more as I researched all the various ways you can do so. But for most use cases, all you really have to do is search for plugins inside of Obsidian and click install when you find them.&lt;/p&gt;

</description>
      <category>obsidian</category>
    </item>
  </channel>
</rss>
