<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://bridgetownrb.com/" version="2.0.5">Bridgetown</generator><link href="https://maquina.app/feed.xml" rel="self" type="application/atom+xml" /><link href="https://maquina.app/" rel="alternate" type="text/html" /><updated>2026-04-16T15:39:50-06:00</updated><id>https://maquina.app/feed.xml</id><title type="html">Maquina</title><subtitle>Comprehensive documentation for Maquina, the open-source Rails framework designed for building modern web applications with a focus on developer experience, performance, and maintainability.</subtitle><author><name>Mario Alberto Chávez Cárdenas</name></author><entry><title type="html">Recuerd0 Now Reads Like a Filesystem</title><link href="https://maquina.app/blog/2026/04/recuerd0-api-release/" rel="alternate" type="text/html" title="Recuerd0 Now Reads Like a Filesystem" /><published>2026-04-07T00:00:00-06:00</published><updated>2026-04-07T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-04-07-recuerd0-api-release.md</id><content type="html" xml:base="https://maquina.app/blog/2026/04/recuerd0-api-release/">&lt;p&gt;&lt;em&gt;The new Recuerd0 API release teaches the memory store to behave like a filesystem — so AI agents already fluent in &lt;code class=&quot;highlighter-rouge&quot;&gt;grep&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;glob&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;read&lt;/code&gt; need no new vocabulary.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://recuerd0.ai/&quot;&gt;Recuerd0&lt;/a&gt; is the persistent memory store for AI coding agents built by Maquina, and this release reshapes how agents read from it. When an AI agent reaches into Recuerd0 for context, the bottleneck has never been storage. It has been &lt;em&gt;how much&lt;/em&gt; the agent has to pull back to get to the one fact it needs. A 2,000-line transcript should not have to fit within the agent’s context window to answer “did we decide to use Postgres?” The new release fixes that — and a handful of other long-standing rough edges — by giving the API the same primitives every coding agent already knows: &lt;code class=&quot;highlighter-rouge&quot;&gt;glob&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;grep&lt;/code&gt;, and ranged &lt;code class=&quot;highlighter-rouge&quot;&gt;read&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is what is new.&lt;/p&gt;

&lt;h2 id=&quot;file-tool-api-glob-grep-and-ranged-read-on-memories&quot;&gt;File-tool API: glob, grep, and ranged read on memories&lt;/h2&gt;

&lt;p&gt;The biggest shift in this release is conceptual. Memories are no longer monolithic blobs that you fetch whole. They are addressable like files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Glob.&lt;/strong&gt; The browse and list endpoints accept a &lt;code class=&quot;highlighter-rouge&quot;&gt;title&lt;/code&gt; glob pattern. &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; matches any sequence of characters, &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; matches a single character. Combined with &lt;code class=&quot;highlighter-rouge&quot;&gt;tags&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;category&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;workspace_id&lt;/code&gt;, the agent can narrow a thousand memories down to the dozen worth looking at without reading any bodies.&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;GET /memories.json?title=Meeting*&amp;amp;tags=design,api&amp;amp;category=decision
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Ranged read.&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;GET /workspaces/:id/memories/:id.json&lt;/code&gt; now accepts &lt;code class=&quot;highlighter-rouge&quot;&gt;line_start&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;line_end&lt;/code&gt; (1-based, inclusive). The response always echoes &lt;code class=&quot;highlighter-rouge&quot;&gt;total_lines&lt;/code&gt;, so the client knows how much memory is available and can compute a tail window in a single follow-up call.&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;GET /workspaces/1/memories/42.json?line_start=40&amp;amp;line_end=55
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is no &lt;code class=&quot;highlighter-rouge&quot;&gt;head=&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;tail=&lt;/code&gt; parameter — and that is deliberate. &lt;code class=&quot;highlighter-rouge&quot;&gt;line_start=1&amp;amp;line_end=20&lt;/code&gt; is “head 20”; &lt;code class=&quot;highlighter-rouge&quot;&gt;line_start=(total_lines - 19)&amp;amp;line_end=total_lines&lt;/code&gt; is “tail 20”. One verb covers both, and the client never has to learn a parallel vocabulary for the same operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grep with line numbers.&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;?mode=grep&amp;amp;q=&amp;lt;query&amp;gt;&lt;/code&gt; switches the same endpoint into a grep response. Instead of returning the body, it returns an array of matches:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_lines&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2174&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;matches&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;line_number&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1247&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Decided: Postgres for the analytics warehouse, SQLite for everything else.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context_before&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;## Database choice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context_after&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Reason: ops simplicity outweighs the JOIN ceiling for our scale.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Optional &lt;code class=&quot;highlighter-rouge&quot;&gt;context&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;before&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;after&lt;/code&gt; parameters control how many surrounding lines to return — capped at 10 each, like &lt;code class=&quot;highlighter-rouge&quot;&gt;grep -C&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;-B&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;-A&lt;/code&gt;. The full-text search endpoint (&lt;code class=&quot;highlighter-rouge&quot;&gt;/search.json&lt;/code&gt;) supports the same grep mode for cross-memory queries.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;two-step recipe&lt;/strong&gt; the agent should reach for: first, use grep to locate the line numbers; then issue a follow-up &lt;code class=&quot;highlighter-rouge&quot;&gt;line_start&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;line_end&lt;/code&gt; call to fetch only the surrounding window. A 2,000-line memory becomes a 20-line answer.&lt;/p&gt;

&lt;h2 id=&quot;memory-categories&quot;&gt;Memory categories&lt;/h2&gt;

&lt;p&gt;Every memory now carries a category: &lt;code class=&quot;highlighter-rouge&quot;&gt;decision&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;discovery&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;preference&lt;/code&gt;, or &lt;code class=&quot;highlighter-rouge&quot;&gt;general&lt;/code&gt; (the default). It is a small thing, but it changes how an agent reasons about what it is reading. A &lt;code class=&quot;highlighter-rouge&quot;&gt;decision&lt;/code&gt; is load-bearing — something the team chose and is sticking with. A &lt;code class=&quot;highlighter-rouge&quot;&gt;discovery&lt;/code&gt; is a fact about the world. A &lt;code class=&quot;highlighter-rouge&quot;&gt;preference&lt;/code&gt; is taste. The agent does not have to infer the difference from prose; it is right there in the metadata, filterable from any list endpoint.&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;GET /memories.json?category=decision&amp;amp;sort=updated_at
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;cross-workspace-memory-links&quot;&gt;Cross-workspace memory links&lt;/h2&gt;

&lt;p&gt;Memories can now reference each other across workspaces with first-class “see also” links. The Rails decision in your &lt;em&gt;Backend&lt;/em&gt; workspace can point to the deployment write-up in &lt;em&gt;Infrastructure&lt;/em&gt; without copying anything. Each memory’s response includes a &lt;code class=&quot;highlighter-rouge&quot;&gt;links_count&lt;/code&gt; so the agent knows there is more context one hop away, and dedicated endpoints under &lt;code class=&quot;highlighter-rouge&quot;&gt;/memories/:id/links&lt;/code&gt; let it list and traverse them.&lt;/p&gt;

&lt;p&gt;This is the connective tissue for context that lives in more than one place — which, in practice, is most context worth keeping.&lt;/p&gt;

&lt;h2 id=&quot;workspace-wake-up-endpoint&quot;&gt;Workspace wake-up endpoint&lt;/h2&gt;

&lt;p&gt;A new endpoint, &lt;code class=&quot;highlighter-rouge&quot;&gt;GET /workspaces/:id/context.json&lt;/code&gt;, returns a compact “wake-up” payload for an agent starting a fresh session: workspace metadata, recent memory titles, and the highlights an agent should know about before it does anything else. It is the answer to “you are picking up where you left off, here is the room you just walked into.”&lt;/p&gt;

&lt;p&gt;Pair it with a Claude Code session-start hook and a new conversation begins with the right context already loaded — no manual &lt;code class=&quot;highlighter-rouge&quot;&gt;recuerd0 memory list&lt;/code&gt; dance, no asking the user to repeat themselves.&lt;/p&gt;

&lt;h2 id=&quot;http-caching-across-the-api&quot;&gt;HTTP caching across the API&lt;/h2&gt;

&lt;p&gt;All read endpoints now emit &lt;code class=&quot;highlighter-rouge&quot;&gt;ETag&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Last-Modified&lt;/code&gt; headers and respect conditional requests. A client that sends &lt;code class=&quot;highlighter-rouge&quot;&gt;If-None-Match&lt;/code&gt; for a memory it already has receives a &lt;code class=&quot;highlighter-rouge&quot;&gt;304 Not Modified&lt;/code&gt; response with an empty body. For agents that re-fetch the same workspace several times in a session, this is a meaningful drop in tokens shipped over the wire — and a meaningful drop in load on the database.&lt;/p&gt;

&lt;p&gt;Grep and ranged-read responses are correctly bypassed by the cache, since they are derived from query parameters that change with each call.&lt;/p&gt;

&lt;h2 id=&quot;cli-recuerd0-memory-read&quot;&gt;CLI: &lt;code class=&quot;highlighter-rouge&quot;&gt;recuerd0 memory read&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/maquina-app/recuerd0-cli&quot;&gt;recuerd0-cli&lt;/a&gt; gains a &lt;code class=&quot;highlighter-rouge&quot;&gt;memory read&lt;/code&gt; command group that wraps the new endpoints so a human (or a terminal-bound agent) can use them without hand-crafting URLs:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;recuerd0 memory &lt;span class=&quot;nb&quot;&gt;read head &lt;/span&gt;42 &lt;span class=&quot;nt&quot;&gt;--lines&lt;/span&gt; 20
recuerd0 memory &lt;span class=&quot;nb&quot;&gt;read tail &lt;/span&gt;42 &lt;span class=&quot;nt&quot;&gt;--lines&lt;/span&gt; 20
recuerd0 memory &lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;lines 42 &lt;span class=&quot;nt&quot;&gt;--start&lt;/span&gt; 100 &lt;span class=&quot;nt&quot;&gt;--end&lt;/span&gt; 140
recuerd0 memory &lt;span class=&quot;nb&quot;&gt;read grep &lt;/span&gt;42 &lt;span class=&quot;s2&quot;&gt;&quot;Postgres&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--context&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--pretty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code class=&quot;highlighter-rouge&quot;&gt;--pretty&lt;/code&gt; mode, the grep subcommand emits a breadcrumb for each hit, suggesting the exact &lt;code class=&quot;highlighter-rouge&quot;&gt;memory read lines&lt;/code&gt;, followed by a call to fetch a window around it. The two-step pattern is right there in the output — no thinking required.&lt;/p&gt;

&lt;h2 id=&quot;agent-guidance-baked-in&quot;&gt;Agent guidance baked in&lt;/h2&gt;

&lt;p&gt;The Claude Code &lt;a href=&quot;https://github.com/maquina-app/rails-claude-code&quot;&gt;recuerd0 agent skill&lt;/a&gt; now ships guidance for &lt;em&gt;when&lt;/em&gt; to use the new primitives, not just how. The dedup-before-write protocol prefers &lt;code class=&quot;highlighter-rouge&quot;&gt;memory read grep&lt;/code&gt; over &lt;code class=&quot;highlighter-rouge&quot;&gt;memory show&lt;/code&gt; for large candidates. The workflow guidelines tell the agent: when &lt;code class=&quot;highlighter-rouge&quot;&gt;total_lines &amp;gt; ~200&lt;/code&gt;, grep first and fetch a window — reserve full reads for memories you genuinely need in their entirety.&lt;/p&gt;

&lt;p&gt;The point of teaching these patterns to the agent is the same as the point of adding them to the API in the first place: make the cheap thing the obvious thing.&lt;/p&gt;

&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;/h2&gt;

&lt;p&gt;Every endpoint above is documented in the public &lt;a href=&quot;https://recuerd0.ai/api_docs&quot;&gt;API reference&lt;/a&gt;, and the &lt;a href=&quot;https://recuerd0.ai/cli&quot;&gt;CLI reference&lt;/a&gt; on recuerd0.ai has been updated to match. The grep→fetch-window workflow is called out as a recipe in both places, with worked examples.&lt;/p&gt;

&lt;h2 id=&quot;why-this-release-matters&quot;&gt;Why this release matters&lt;/h2&gt;

&lt;p&gt;Coding agents are getting fluent. They already know how to use &lt;code class=&quot;highlighter-rouge&quot;&gt;glob&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;grep&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;read&lt;/code&gt; — those primitives are how they navigate filesystems every day. Recuerd0’s job is not to invent a new vocabulary for context retrieval; it is to look enough like a filesystem that agents do not have to learn one.&lt;/p&gt;

&lt;p&gt;This release is that bet, made concrete. A memory is now something you can grep. A workspace is now something you can wake up in. A long transcript no longer has to fit entirely within a context window just so the agent can quote one line from it.&lt;/p&gt;

&lt;h2 id=&quot;get-the-update&quot;&gt;Get the update&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;SaaS users on &lt;a href=&quot;https://recuerd0.ai/&quot;&gt;recuerd0.ai&lt;/a&gt;:&lt;/strong&gt; the new endpoints are live now. No action needed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Self-hosters:&lt;/strong&gt; pull the latest &lt;a href=&quot;https://github.com/maquina-app/recuerd0&quot;&gt;recuerd0&lt;/a&gt; image (or &lt;code class=&quot;highlighter-rouge&quot;&gt;git pull&lt;/code&gt; and redeploy with Kamal). Run migrations to pick up the new &lt;code class=&quot;highlighter-rouge&quot;&gt;category&lt;/code&gt; column and the &lt;code class=&quot;highlighter-rouge&quot;&gt;memory_links&lt;/code&gt; table.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;CLI users:&lt;/strong&gt; you must update to the latest version to get the new &lt;code class=&quot;highlighter-rouge&quot;&gt;memory read&lt;/code&gt; commands — &lt;code class=&quot;highlighter-rouge&quot;&gt;brew upgrade recuerd0-cli&lt;/code&gt; (or grab the latest binary from &lt;a href=&quot;https://github.com/maquina-app/recuerd0-cli/releases&quot;&gt;recuerd0-cli releases&lt;/a&gt;). Older CLI versions will not expose the new functionality.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Claude Code users:&lt;/strong&gt; update the &lt;strong&gt;recuerd0 plugin&lt;/strong&gt; from the Claude Code marketplace to pick up the new agent guidance and command reference. Without the plugin update, the agent will keep using the old &lt;code class=&quot;highlighter-rouge&quot;&gt;memory show&lt;/code&gt; flow instead of the new grep-first patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;frequently-asked-questions&quot;&gt;Frequently asked questions&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How do I grep a Recuerd0 memory?&lt;/strong&gt;
Send &lt;code class=&quot;highlighter-rouge&quot;&gt;GET /workspaces/:id/memories/:id.json?mode=grep&amp;amp;q=&amp;lt;query&amp;gt;&lt;/code&gt;. The response returns line numbers and surrounding context instead of the full body. From the CLI: &lt;code class=&quot;highlighter-rouge&quot;&gt;recuerd0 memory read grep &amp;lt;id&amp;gt; &quot;&amp;lt;query&amp;gt;&quot; --context 2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between ranged read and grep mode?&lt;/strong&gt;
Grep mode finds &lt;em&gt;where&lt;/em&gt; a string appears (returns matching line numbers with context). Ranged read fetches &lt;em&gt;what&lt;/em&gt; is at known line numbers via &lt;code class=&quot;highlighter-rouge&quot;&gt;line_start&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;line_end&lt;/code&gt;. The recommended workflow is grep first to locate, then ranged read to fetch a window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I have to update the CLI and Claude Code plugin?&lt;/strong&gt;
Yes. The new &lt;code class=&quot;highlighter-rouge&quot;&gt;memory read&lt;/code&gt; commands ship in the latest &lt;a href=&quot;https://github.com/maquina-app/recuerd0-cli&quot;&gt;recuerd0-cli&lt;/a&gt;, and the grep-first agent guidance ships in the updated recuerd0 plugin in the Claude Code marketplace. Older versions will keep working but won’t expose the new endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are memory categories used for?&lt;/strong&gt;
Each memory is tagged as &lt;code class=&quot;highlighter-rouge&quot;&gt;decision&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;discovery&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;preference&lt;/code&gt;, or &lt;code class=&quot;highlighter-rouge&quot;&gt;general&lt;/code&gt;. Agents (and humans) can filter by category to find load-bearing decisions without sifting through general notes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does HTTP caching apply to grep queries?&lt;/strong&gt;
No. ETag/Last-Modified caching applies to whole-memory and list reads. Grep and ranged-read responses are derived from query parameters and bypass the cache by design.&lt;/p&gt;

&lt;h2 id=&quot;related-reading&quot;&gt;Related reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/announcing-recuerd0/&quot;&gt;Announcing Recuerd0&lt;/a&gt; — the original launch and the problem we set out to solve.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/recuerd0-source-code-now-available/&quot;&gt;Recuerd0 source code is now available&lt;/a&gt; — how to self-host under OSASSY.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Maquina open-source projects&lt;/a&gt; — the rest of the Rails and AI tooling we maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Recuerd0 is built by &lt;a href=&quot;https://maquina.app/&quot;&gt;Maquina&lt;/a&gt;. Source available under &lt;a href=&quot;https://osaasy.dev/&quot;&gt;OSASSY license&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-recuerd0-filesystem.jpg" /><media:content medium="image" url="https://maquina.app/images/og-recuerd0-filesystem.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">MVP Creator: From Idea to Documents in Three Prompts</title><link href="https://maquina.app/blog/2026/03/mvp-creator-from-idea-to-documents/" rel="alternate" type="text/html" title="MVP Creator: From Idea to Documents in Three Prompts" /><published>2026-03-23T00:00:00-06:00</published><updated>2026-03-23T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-03-23-mvp-creator-from-idea-to-documents.md</id><content type="html" xml:base="https://maquina.app/blog/2026/03/mvp-creator-from-idea-to-documents/">&lt;p&gt;Before writing a single line of code, I need to understand what I’m building. Not abstractly — concretely: who the users are, what the real problem is, what the app is called, what voice it has, what technical decisions I’m making from the start. For a long time, that work happened informally — in scattered notes, in my head, or spread across different roles on a team: product knowledge in one conversation, brand direction in another, architecture in some document nobody kept updated. Now I formalize it with an agent called MVP Creator.&lt;/p&gt;

&lt;p&gt;In this video — the first in a series about my personal process with AI — I show how I use MVP Creator to generate the complete set of foundation documents for a new project: research report, business plan, brand guide, and technical guide. All of it with three prompts, from an initial idea to documentation ready to hand off to Claude Code.&lt;/p&gt;

&lt;script type=&quot;application/ld+json&quot;&gt;
{
  &quot;@context&quot;: &quot;https://schema.org&quot;,
  &quot;@type&quot;: &quot;VideoObject&quot;,
  &quot;name&quot;: &quot;MVP Creator: From Idea to Documents in Three Prompts&quot;,
  &quot;description&quot;: &quot;Full walkthrough of the MVP Creator Claude Code plugin. From an initial app idea to research report, business plan, brand guide, and technical guide in three prompts.&quot;,
  &quot;thumbnailUrl&quot;: &quot;https://maquina.app/images/og-blog-mvp-creator.jpg&quot;,
  &quot;uploadDate&quot;: &quot;2026-03-23&quot;,
  &quot;embedUrl&quot;: &quot;https://player.vimeo.com/video/1175986278&quot;,
  &quot;duration&quot;: &quot;PT40M&quot;,
  &quot;publisher&quot;: {
    &quot;@type&quot;: &quot;Organization&quot;,
    &quot;name&quot;: &quot;Maquina&quot;,
    &quot;logo&quot;: {
      &quot;@type&quot;: &quot;ImageObject&quot;,
      &quot;url&quot;: &quot;https://maquina.app/images/logo.png&quot;
    }
  }
}
&lt;/script&gt;

&lt;div class=&quot;not-prose my-8 overflow-hidden rounded-2xl&quot;&gt;
&lt;div style=&quot;padding:56.25% 0 0 0;position:relative;&quot;&gt;&lt;iframe src=&quot;https://player.vimeo.com/video/1175986278?badge=0&amp;amp;autopause=0&amp;amp;player_id=0&amp;amp;app_id=58479&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; style=&quot;position:absolute;top:0;left:0;width:100%;height:100%;&quot; title=&quot;MVP Creator: From Idea to Documents in Three Prompts&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;script src=&quot;https://player.vimeo.com/api/player.js&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;h2 id=&quot;the-three-prompts&quot;&gt;The Three Prompts&lt;/h2&gt;

&lt;p&gt;The example in the video is a photo delivery platform for professional photographers. These are the exact prompts I use, in order.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;prompt-1--the-idea-and-context&quot;&gt;Prompt 1 — The Idea and Context&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Help me create an MVP for a photo delivery platform for professional photographers.
Think of it as a private gallery where photographers deliver finished work to clients.

The core concept: a photographer creates a Project (for a client or personal work),
organizes photos into Collections within that project, and shares the gallery via
single-use expirable links. Invited clients can view, comment, like, and download
photos in their preferred quality.

Key features:
- Projects with collections and high-resolution photo uploads
- Active Storage for thumbnail + quality variants (low/medium/high)
- Reorderable photos within collections, cover photo per collection
- Shareable links: single-use, expire in 7 days, create read-only sessions
- Download: single photo or multi-select as zip, with quality choice

Target users: freelance and studio photographers in Latin America
Language: Spanish-first, English secondary
App name: I&apos;m thinking &quot;Liminal&quot; — open to suggestions

Research these competitors: https://www.pic-time.com and
https://www.picdrop.com/web — also look at how Google Drive handles
shared folder UX as a reference point.

Use the MVP Creator skill to generate the full documentation set.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this prompt the agent launches competitor research, runs through the discovery questions, and generates the four foundation documents: research report, business plan, brand guide, and technical guide.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;prompt-2--brand-voice&quot;&gt;Prompt 2 — Brand Voice&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Based on everything we&apos;ve defined about Liminal — the LATAM market, photographers
delivering work to clients, the quiet confidence of the name itself — write a brand
voice document.

The voice should feel like a photographer who has found their style and doesn&apos;t need
to announce it. Not austere, but economical. Someone who chooses words the way they
choose light — deliberately, with care for what gets left out as much as what stays in.

Professionalism here means craft, not corporate. The app handles something personal
— a photographer&apos;s finished work, a client&apos;s important memories. The voice should
honor that weight without becoming precious about it.

Influences: the way Magnum Photos writes about their work. The directness of a good
photo caption. Not the breathless enthusiasm of a SaaS landing page.

The document should include:
- Core personality traits (3–4, with explanation)
- Tone spectrum (when to be warmer vs. more spare)
- Vocabulary: words we use, words we avoid
- UI microcopy examples (button labels, empty states, error messages)
- Both Spanish and English examples side by side
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This second prompt goes straight to the character of the app. A brand voice guide is a document that rarely gets produced in an MVP phase — and it’s one of the most useful when the time comes to write microcopy or define how the app speaks to its users.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;prompt-3--ui-mocks&quot;&gt;Prompt 3 — UI Mocks&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Using the frontend-design skill, create UI mocks for Liminal&apos;s critical screens.
Pull from the brand guide already established and the brand voice: quiet craft,
deliberate, editorial — not SaaS.

Prioritize these screens in order:

Client-facing (unauthenticated, via share link):
1. Gallery landing — the first thing a client sees when they open their link.
2. Collection view — browsing photos within a collection, with like, comment,
   and download interactions visible.
3. Download selection — choosing photos and quality before downloading as zip.

Photographer-facing (authenticated):
4. Project dashboard — list of projects with status at a glance.
5. Collection editor — uploading photos, reordering, setting cover photo.
6. Share link manager — creating and tracking links, seeing which have been used.

For each screen:
- Design for desktop first, note mobile considerations
- Show real placeholder content — no Lorem Ipsum
- Embed a short design rationale note explaining the key decision made for that screen

Aesthetic direction: editorial photography magazine meets quiet utility. The UI
should feel like it was designed by someone who photographs, not someone who ships
dashboards.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The third prompt uses the &lt;code class=&quot;highlighter-rouge&quot;&gt;frontend-design&lt;/code&gt; skill together with &lt;a href=&quot;https://maquina.app/documentation/components/&quot;&gt;Maquina Components&lt;/a&gt; to generate HTML mocks of the critical screens. The result isn’t a Figma file — it’s a functional visual reference, coherent with the brand guide, before opening the editor.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-result&quot;&gt;The Result&lt;/h2&gt;

&lt;p&gt;Three prompts. Six documents. Mocks of the main screens. All the context needed to hand off to Claude Code and start generating code with direction.&lt;/p&gt;

&lt;p&gt;It’s the same process I used to build &lt;a href=&quot;https://turesto.app/en&quot;&gt;Resto&lt;/a&gt;, a personal finance app based on the Japanese Kakeibo method.&lt;/p&gt;

&lt;p&gt;The video runs 40 minutes. It’s not an accelerated demo — it’s the real process, iterations and corrections included.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;p&gt;All plugins are available in the &lt;a href=&quot;https://github.com/maquina-app/rails-claude-code&quot;&gt;maquina-app/rails-claude-code&lt;/a&gt; repository. Full documentation at &lt;a href=&quot;/documentation/ai-tools/mvp-creator/&quot;&gt;MVP Creator — Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install MVP Creator in Claude Code:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Add the marketplace&lt;/span&gt;
/plugin marketplace add maquina-app/rails-claude-code

&lt;span class=&quot;c&quot;&gt;# Install the plugin&lt;/span&gt;
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mvp-creator@maquina
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To install the full set of plugins used in this series:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;rails-simplifier@maquina
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;rails-upgrade-assistant@maquina
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;maquina-ui-standards@maquina
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mvp-creator@maquina
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;better-stimulus@maquina
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;spec-driven-development@maquina
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the Claude graphical interface, download the repository as a zip, extract the &lt;code class=&quot;highlighter-rouge&quot;&gt;mvp-creator&lt;/code&gt; folder, rename the extension to &lt;code class=&quot;highlighter-rouge&quot;&gt;.skill&lt;/code&gt;, and drag it into the Claude window to install it.&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-mvp-creator.jpg" /><media:content medium="image" url="https://maquina.app/images/og-blog-mvp-creator.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Maquina Generators: From rails new to Production-Ready</title><link href="https://maquina.app/blog/2026/03/maquina-generators-production-ready-rails-setup/" rel="alternate" type="text/html" title="Maquina Generators: From rails new to Production-Ready" /><published>2026-03-13T00:00:00-06:00</published><updated>2026-03-13T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-03-13-maquina-generators-production-ready-rails-setup.md</id><content type="html" xml:base="https://maquina.app/blog/2026/03/maquina-generators-production-ready-rails-setup/">&lt;p&gt;Every Rails project starts the same way. You run &lt;code class=&quot;highlighter-rouge&quot;&gt;rails new&lt;/code&gt;, you get a clean app with sensible defaults, and then you do the setup work before you can write application code. Authentication with signup and multi-tenancy. Rate limiting. Background jobs with a dashboard. Error tracking. Mailer templates. Security headers. It’s repetitive, sure, but it’s the work that gets your app to the point where you can build the thing you actually sat down to build.&lt;/p&gt;

&lt;p&gt;I’ve done this enough times to know exactly what’s coming. The order changes, the names of the models drift slightly, but the shape of the work is identical. It’s not that Rails is missing anything — it’s that the space between &lt;code class=&quot;highlighter-rouge&quot;&gt;rails new&lt;/code&gt; and “ready to build features” is full of choices that are mostly already made. You just have to type them out each time.&lt;/p&gt;

&lt;p&gt;Maquina Generators automate that setup. One command after &lt;code class=&quot;highlighter-rouge&quot;&gt;rails new&lt;/code&gt;, and you have authentication, multi-tenancy, roles, job processing, error tracking, request protection, and ops dashboards. All generated into your app as plain Rails code. No runtime dependency.&lt;/p&gt;

&lt;h2 id=&quot;what-maquina-generators-do&quot;&gt;What Maquina Generators Do&lt;/h2&gt;

&lt;p&gt;The gem lives in your development group. It generates standalone application code — models, controllers, views, migrations, initializers, mailers — and then you can delete the gem. Nothing it produces requires the gem at runtime. No engine mounts, no middleware injection, no monkey patches. Just files in your app that you own completely.&lt;/p&gt;

&lt;p&gt;The workflow is five commands:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails new myapp &lt;span class=&quot;nt&quot;&gt;--css&lt;/span&gt; tailwind
bundle add maquina-generators &lt;span class=&quot;nt&quot;&gt;--group&lt;/span&gt; development
rails generate maquina:app &lt;span class=&quot;nt&quot;&gt;--auth&lt;/span&gt; clave
bin/rails db:migrate
bin/dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. Auth with email verification codes, an Account model with roles, Rack Attack blocking scanners and throttling logins, Solid Queue with a Procfile, Solid Errors catching exceptions, Mission Control monitoring your jobs — all wired up, all running.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/generators-screenshot-1.png&quot; alt=&quot;Generated app homepage showing authentication, background jobs, error tracking, rate limiting, caching, and real-time features&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Seven generators handle the pieces:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Generator&lt;/th&gt;
      &lt;th&gt;Purpose&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;App&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Full application setup — orchestrates everything below&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Clave&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Passwordless email-code authentication&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Registration&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Password-based auth with accounts and roles&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Rack Attack&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Request protection and IP throttling&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Solid Queue&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Background job processing with separate database&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Solid Errors&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Error tracking dashboard&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Mission Control&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Job queue monitoring dashboard&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The App generator is the orchestrator. It runs whichever auth generator you choose, then all the infrastructure generators in sequence. You can also run each generator independently if you only need part of the stack.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;/documentation/generators/&quot;&gt;full documentation&lt;/a&gt; covers every generator, option, and generated file in detail.&lt;/p&gt;

&lt;h2 id=&quot;two-authentication-options&quot;&gt;Two Authentication Options&lt;/h2&gt;

&lt;p&gt;Rails 8’s built-in &lt;code class=&quot;highlighter-rouge&quot;&gt;rails generate authentication&lt;/code&gt; gives you login. It doesn’t give you signup. It doesn’t give you accounts, roles, or multi-tenancy. For most applications, login alone isn’t enough.&lt;/p&gt;

&lt;p&gt;Maquina Generators offer two complete authentication systems that pick up where Rails leaves off.&lt;/p&gt;

&lt;h3 id=&quot;clave-passwordless&quot;&gt;Clave: Passwordless&lt;/h3&gt;

&lt;p&gt;Clave implements passwordless authentication using email verification codes. The user enters their email, receives a 6-digit hexadecimal code, enters the code, and they’re in. No passwords to store, no password resets to build, no complexity requirements to argue about.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;User enters email → receives 6-digit code → enters code → signed in
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Codes expire in 15 minutes. There’s a 15-minute cooldown before a resend. Login attempts are rate-limited to 10 per 3 minutes. Sessions last 30 days by default. Plus characters are blocked in email addresses to prevent alias attacks.&lt;/p&gt;

&lt;p&gt;Beyond sign-in, Clave generates a full multi-tenancy layer. Every user belongs to an Account. The first user who creates an account becomes its admin. A role enum — admin or member — handles authorization from there.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# The signed-in user&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;account&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# The user&apos;s account&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;admin?&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# Check role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You scope queries through the account, and cross-tenant access is prevented at the model level:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;vi&quot;&gt;@projects&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;projects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Clave generates models, controllers, a mailer with HTML and text templates, a daily cleanup job for expired sessions and codes, a test helper with &lt;code class=&quot;highlighter-rouge&quot;&gt;sign_in_as(user)&lt;/code&gt;, and full i18n support in English and Spanish.&lt;/p&gt;

&lt;h3 id=&quot;registration-password-based&quot;&gt;Registration: Password-Based&lt;/h3&gt;

&lt;p&gt;If you prefer passwords, the Registration generator builds on Rails 8’s authentication. It runs &lt;code class=&quot;highlighter-rouge&quot;&gt;rails generate authentication&lt;/code&gt; first, then adds what’s missing: an Account model, &lt;code class=&quot;highlighter-rouge&quot;&gt;belongs_to :account&lt;/code&gt; on User, the role enum, a RegistrationsController that creates an Account and User in a single transaction, and Tailwind-styled views.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistrationsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;allow_unauthenticated_access&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;rate_limit&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;within: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: :create&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transaction&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:account_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;email_address: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:email_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;password: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;role: :admin&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_new_session_for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root_path&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/generators-screenshot-2.png&quot; alt=&quot;Generated sign-in page with email and password fields&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Same &lt;code class=&quot;highlighter-rouge&quot;&gt;Current.user&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Current.account&lt;/code&gt;, and role-based authorization as Clave. The multi-tenancy pattern is identical — only the sign-in mechanism differs.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;/documentation/generators/&quot;&gt;generators documentation&lt;/a&gt; covers every option, model, and controller for both auth systems.&lt;/p&gt;

&lt;h2 id=&quot;the-ops-layer&quot;&gt;The Ops Layer&lt;/h2&gt;

&lt;p&gt;Authentication is the most visible piece, but the App generator does more than auth. It sets up a complete operational layer that most Rails apps need but few have on day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rack Attack&lt;/strong&gt; gets configured with real-world defaults. PHP file requests, WordPress scanning paths, &lt;code class=&quot;highlighter-rouge&quot;&gt;.env&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;.git&lt;/code&gt; probes — all blocked immediately. Sensitive paths like &lt;code class=&quot;highlighter-rouge&quot;&gt;/cgi-bin&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;/phpmyadmin&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;/actuator&lt;/code&gt; return 403. General traffic is throttled to 300 requests per 5 minutes per IP, with asset paths exempted. Login endpoints get tighter limits: 5 attempts per 20 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solid Queue&lt;/strong&gt; is set up as the Active Job backend with its own SQLite database, a Procfile entry for the worker process, and a recurring schedule that runs the authentication cleanup job daily at 3am. The configuration lives in &lt;code class=&quot;highlighter-rouge&quot;&gt;config/solid_queue.yml&lt;/code&gt; — three worker threads, half-second polling, standard dispatching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solid Errors&lt;/strong&gt; and &lt;strong&gt;Mission Control Jobs&lt;/strong&gt; get mounted as dashboards with custom Tailwind views. Mission Control alone has 41 view files — job listings, queue status, worker monitoring, recurring task management — all styled to match your application instead of looking like a default engine mount.&lt;/p&gt;

&lt;p&gt;Both dashboards share the same HTTP basic auth credentials:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# bin/rails credentials:edit&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;backstage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;your_secure_password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One set of credentials, stored in Rails credentials. Environment variable fallbacks if you prefer. After running the generators, you have &lt;code class=&quot;highlighter-rouge&quot;&gt;/admin/solid_errors&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;/admin/mission_control_jobs&lt;/code&gt; working from the first &lt;code class=&quot;highlighter-rouge&quot;&gt;bin/dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/generators-screenshot-3.png&quot; alt=&quot;Admin tools section showing Solid Errors and Mission Control Jobs dashboards after sign-in&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The App generator also sets up multi-database configuration — separate SQLite databases for the queue, cache, cable, and errors — installs Active Storage and Action Text, configures Turbo morphing, adds brakeman and Standard for code quality, and creates a HomeController with a root route. It’s the full post-&lt;code class=&quot;highlighter-rouge&quot;&gt;rails new&lt;/code&gt; checklist, automated.&lt;/p&gt;

&lt;h2 id=&quot;own-the-code&quot;&gt;Own the Code&lt;/h2&gt;

&lt;p&gt;This is the part that matters most. Maquina Generators is a development-only gem. It generates code into your application and then it’s done. You can — and should — delete it from your Gemfile once you’ve run the generators.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile — remove after generating&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:development&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;maquina-generators&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every file it produces is a standard Rails file in a standard location. Models in &lt;code class=&quot;highlighter-rouge&quot;&gt;app/models&lt;/code&gt;, controllers in &lt;code class=&quot;highlighter-rouge&quot;&gt;app/controllers&lt;/code&gt;, views in &lt;code class=&quot;highlighter-rouge&quot;&gt;app/views&lt;/code&gt;, initializers in &lt;code class=&quot;highlighter-rouge&quot;&gt;config/initializers&lt;/code&gt;. No engine, no namespace, no gem dependency at runtime. If you want to change how sessions expire, you edit &lt;code class=&quot;highlighter-rouge&quot;&gt;app/controllers/concerns/authentication.rb&lt;/code&gt;. If you want different Rack Attack rules, you edit &lt;code class=&quot;highlighter-rouge&quot;&gt;config/initializers/rack_attack.rb&lt;/code&gt;. If you want to add a third role beyond admin and member, you update the enum on User.&lt;/p&gt;

&lt;p&gt;There’s no DSL to learn, no configuration file to maintain, no version upgrades to track. The generated code follows Rails conventions because it &lt;em&gt;is&lt;/em&gt; Rails code. You can read every line, understand every decision, and change anything that doesn’t fit your project.&lt;/p&gt;

&lt;p&gt;This connects to the broader Maquina ecosystem. The generators set up the foundation — auth, security, ops tooling. &lt;a href=&quot;/documentation/components/&quot;&gt;Maquina Components&lt;/a&gt; handles the UI layer with ViewComponent-based partials that the App generator installs automatically. When you start building features on top of this foundation, &lt;a href=&quot;/documentation/ai-tools/rails-simplifier/&quot;&gt;Rails Simplifier&lt;/a&gt; keeps AI-generated code idiomatic, and the &lt;a href=&quot;/documentation/ai-tools/rails-mcp-server/&quot;&gt;MCP Server&lt;/a&gt; gives AI tools visibility into your codebase structure.&lt;/p&gt;

&lt;p&gt;Each tool is independent. Use one, use all, use none. No lock-in at any layer.&lt;/p&gt;

&lt;h2 id=&quot;get-started&quot;&gt;Get Started&lt;/h2&gt;

&lt;p&gt;Install the gem and run the app generator:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails new myapp &lt;span class=&quot;nt&quot;&gt;--css&lt;/span&gt; tailwind
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;myapp
bundle add maquina-generators &lt;span class=&quot;nt&quot;&gt;--group&lt;/span&gt; development
rails generate maquina:app &lt;span class=&quot;nt&quot;&gt;--auth&lt;/span&gt; clave
bin/rails db:migrate
bin/dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Choose &lt;code class=&quot;highlighter-rouge&quot;&gt;--auth clave&lt;/code&gt; for passwordless, &lt;code class=&quot;highlighter-rouge&quot;&gt;--auth registration&lt;/code&gt; for passwords, or &lt;code class=&quot;highlighter-rouge&quot;&gt;--auth none&lt;/code&gt; if you want the infrastructure without authentication.&lt;/p&gt;

&lt;p&gt;Full documentation is at &lt;a href=&quot;/documentation/generators/&quot;&gt;maquina.app/documentation/generators&lt;/a&gt;. Source code is on &lt;a href=&quot;https://github.com/maquina-app/maquina_generators&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-announce-maquina-generators.png" /><media:content medium="image" url="https://maquina.app/images/og-blog-announce-maquina-generators.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Recuerd0 Source Code Is Now Available</title><link href="https://maquina.app/blog/2026/02/recuerd0-source-code-now-available/" rel="alternate" type="text/html" title="Recuerd0 Source Code Is Now Available" /><published>2026-02-21T00:00:00-06:00</published><updated>2026-02-21T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-02-21-recuerd0-source-code-now-available.md</id><content type="html" xml:base="https://maquina.app/blog/2026/02/recuerd0-source-code-now-available/">&lt;p&gt;&lt;em&gt;The self-hosted promise is fulfilled — Recuerd0’s source code is on GitHub.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;When we &lt;a href=&quot;/blog/2026/02/15/announcing-recuerd0/&quot;&gt;announced Recuerd0&lt;/a&gt;, we said the self-hosted version would be available pretty soon. Today it is. The full source code is on &lt;a href=&quot;https://github.com/maquina-app/recuerd0&quot;&gt;GitHub&lt;/a&gt; under the OSASSY license.&lt;/p&gt;

&lt;p&gt;This is not a stripped-down edition. It’s the same codebase that runs &lt;a href=&quot;https://recuerd0.ai/&quot;&gt;recuerd0.ai&lt;/a&gt; — every feature, every endpoint, every migration.&lt;/p&gt;

&lt;h2 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h2&gt;

&lt;p&gt;Recuerd0 is a Rails 8.1 application running on Ruby 4.0. The entire stack leans into the One Person Framework philosophy: minimize infrastructure, eliminate external dependencies, ship with confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQLite for everything.&lt;/strong&gt; Data, cache, queue, and cable — all backed by SQLite. No Postgres. No Redis. Solid Queue handles background jobs, Solid Cache handles caching, and Solid Cable handles WebSocket connections. One database engine, zero extra services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Node.js.&lt;/strong&gt; The frontend uses Propshaft for asset delivery and Importmaps for JavaScript modules. Hotwire (Turbo + Stimulus) handles interactivity. Tailwind CSS 4 handles styling. The entire frontend pipeline runs without a JS build step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full-text search with FTS5.&lt;/strong&gt; Search is powered by SQLite’s FTS5 extension — no vector database, no embeddings, no RAG pipeline. The index updates on every write, returns results in milliseconds, and is fully deterministic. The agent decides what to search for; the database does the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory versioning.&lt;/strong&gt; Every memory supports a flat branching model — create new versions from any point in history. Soft deletion with 30-day retention means nothing disappears by accident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-tenancy.&lt;/strong&gt; The Account model supports multiple tenants. In single-tenant mode (the default for self-hosted), public registration is disabled — you control who has access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI components.&lt;/strong&gt; The interface is built with the &lt;a href=&quot;https://github.com/maquina-app/maquina_components&quot;&gt;maquina-components&lt;/a&gt; gem, the same component library used across all Maquina projects.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Two paths to self-host:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker image.&lt;/strong&gt; Pull the ready-to-use Docker image and deploy. Configure your environment variables and you’re running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From source.&lt;/strong&gt; Clone the &lt;a href=&quot;https://github.com/maquina-app/recuerd0&quot;&gt;repository&lt;/a&gt;, configure Kamal 2.x, and deploy to your server. The included Dockerfile and Kamal configuration handle the rest. Thruster sits in front of Puma, and &lt;code class=&quot;highlighter-rouge&quot;&gt;SOLID_QUEUE_IN_PUMA=true&lt;/code&gt; runs background jobs in-process — one container, one process, everything included.&lt;/p&gt;

&lt;p&gt;Single-tenant mode is the default. No public registration, no setup wizard. Deploy, create your account, start curating context.&lt;/p&gt;

&lt;h2 id=&quot;license&quot;&gt;License&lt;/h2&gt;

&lt;p&gt;Recuerd0 is released under the &lt;a href=&quot;https://osaasy.dev/&quot;&gt;OSASSY license&lt;/a&gt;. It’s essentially MIT with one restriction: you can’t take the code and offer it as a competing hosted service. The same model 37signals uses. Deploy it on your infrastructure, modify it, use it internally — free forever.&lt;/p&gt;

&lt;h2 id=&quot;not-interested-in-self-hosting&quot;&gt;Not interested in self-hosting?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://recuerd0.ai/&quot;&gt;Recuerd0 SaaS&lt;/a&gt; is $15/month for up to 10 users — managed hosting, automatic backups, and updates. Read the &lt;a href=&quot;/blog/2026/02/15/announcing-recuerd0/&quot;&gt;full product announcement&lt;/a&gt; for the complete story.&lt;/p&gt;

&lt;p&gt;The source is on GitHub. Do what you want with it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/maquina-app/recuerd0&quot;&gt;View the repository →&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Recuerd0 is built by &lt;a href=&quot;https://maquina.app/&quot;&gt;Maquina&lt;/a&gt;. Source available under &lt;a href=&quot;https://osaasy.dev/&quot;&gt;OSASSY license&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/recuerd0-og.png" /><media:content medium="image" url="https://maquina.app/images/recuerd0-og.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Announcing Recuerd0: A Knowledge Base for AI Tool Context</title><link href="https://maquina.app/blog/2026/02/announcing-recuerd0/" rel="alternate" type="text/html" title="Announcing Recuerd0: A Knowledge Base for AI Tool Context" /><published>2026-02-15T00:00:00-06:00</published><updated>2026-02-15T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-02-15-announcing-recuerd0.md</id><content type="html" xml:base="https://maquina.app/blog/2026/02/announcing-recuerd0/">&lt;p&gt;&lt;em&gt;Organize, version, and serve project context to any LLM — from Claude Code to Cursor to ChatGPT.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Every AI coding tool starts each session with amnesia. Your architecture decisions, naming conventions, and deployment quirks — none of it carries over. You re-explain the same context with the same tools every single day.&lt;/p&gt;

&lt;p&gt;The common workarounds are CLAUDE.md files, .cursorrules, AGENTS.md — each tool with its own configuration format. You end up duplicating knowledge across multiple places. They drift apart. Your Claude Code config says one thing; your Cursor rules say another.&lt;/p&gt;

&lt;p&gt;Recuerd0 is a dedicated knowledge base for managing the context your AI tools consume. You curate project knowledge once and serve it to every tool via REST API.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/recuerd0/home-01.png&quot; alt=&quot;Recuerd0 homepage — the knowledge base your AI tools deserve&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Workspaces&lt;/strong&gt; group knowledge by project or domain. Backend conventions in one workspace, frontend patterns in another, org-wide standards in a shared workspace.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/recuerd0/workspaces-02.png&quot; alt=&quot;Workspace view showing pinned memories with tags and descriptions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memories&lt;/strong&gt; are versioned markdown documents with titles, tags, and full history. When conventions evolve, you create a new version — like Git for context. Branch from any version, track how decisions changed, and never lose the rationale.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/recuerd0/memory-03.png&quot; alt=&quot;Memory detail showing rendered markdown content with version history and tags&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access&lt;/strong&gt; is through a REST API with Bearer token authentication. Any tool that can make an HTTP request reads from the same source. There’s also a CLI for terminal workflows and a Claude Code plugin for tighter integration.&lt;/p&gt;

&lt;p&gt;Search uses the database’s full-text search with millisecond performance. No embeddings, no vector database, no RAG pipeline. The agent decides what to search for and how to refine the search. The index updates on every write, is deterministic, and requires zero infrastructure beyond the database.&lt;/p&gt;

&lt;h2 id=&quot;architecture-decisions&quot;&gt;Architecture decisions&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Human-curated, not auto-captured.&lt;/strong&gt; Automatic knowledge capture sounds appealing, but it produces noisy results — context-specific fixes that don’t generalize, contradictory items as conventions evolve. The human decides what’s worth persisting. The team reviews and evolves it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool-agnostic by design.&lt;/strong&gt; We built an API, not a plugin for one tool. Your knowledge base survives any tool change. Claude Code, Cursor, ChatGPT, Windsurf, custom scripts, CI/CD pipelines — same context, same source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small and focused.&lt;/strong&gt; Recuerd0 is designed for a small set of focused memories per workspace philosophy, not thousands of files. The constraint forces curation. When the workspace is focused, the right answer is obvious without sophisticated search algorithms.&lt;/p&gt;

&lt;h2 id=&quot;pricing&quot;&gt;Pricing&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recuerd0 SaaS&lt;/strong&gt; is $15/month for up to 10 users. Managed hosting, automatic backups, updates, and email support. Create an account and start in minutes at &lt;a href=&quot;https://recuerd0.ai/&quot;&gt;recuerd0.ai&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Teams of 6 or more can contact us for custom plans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-hosted&lt;/strong&gt; is available under the &lt;a href=&quot;https://osaasy.dev/&quot;&gt;OSASSY license&lt;/a&gt; — the same model 37signals uses for Fizzy. It’s essentially MIT with one addition: you can’t take the code and offer it as a competing hosted service. Deploy on your server, modify the code, use it internally — free forever.&lt;/p&gt;

&lt;p&gt;The self-hosted version is not available at launch, but it will be available pretty soon.&lt;/p&gt;

&lt;h2 id=&quot;get-started&quot;&gt;Get started&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://recuerd0.ai/api-docs&quot;&gt;API documentation&lt;/a&gt; covers every endpoint. The &lt;a href=&quot;https://recuerd0.ai/cli&quot;&gt;CLI reference&lt;/a&gt; has installation and commands. The &lt;a href=&quot;https://recuerd0.ai/agents&quot;&gt;agent workflows guide&lt;/a&gt; shows how to integrate with Claude Code, Cursor, and other tools.&lt;/p&gt;

&lt;p&gt;Context engineering has become a core developer skill. It deserves a dedicated tool.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://recuerd0.ai/&quot;&gt;Start with Recuerd0 SaaS →&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Recuerd0 is built by &lt;a href=&quot;https://maquina.app/&quot;&gt;Maquina&lt;/a&gt;. Source available under &lt;a href=&quot;https://osaasy.dev/&quot;&gt;OSASSY license&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/recuerd0-og.png" /><media:content medium="image" url="https://maquina.app/images/recuerd0-og.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Maquina Components 0.4.0: Taming Turbo</title><link href="https://maquina.app/blog/2026/02/maquina-components-0-4-0-turbo-compatibility/" rel="alternate" type="text/html" title="Maquina Components 0.4.0: Taming Turbo" /><published>2026-02-13T00:00:00-06:00</published><updated>2026-02-13T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-02-13-maquina-components-0-4-0-turbo-compatibility.md</id><content type="html" xml:base="https://maquina.app/blog/2026/02/maquina-components-0-4-0-turbo-compatibility/">&lt;p&gt;I was working on a Rails application—standard CRUD with a sidebar and a few interactive menus. Everything worked on first load. Then I navigated away and came back. The sidebar was gone. I opened a dropdown, clicked a Turbo link, hit the back button. The dropdown was still open, sitting there on top of a page that had already moved on.&lt;/p&gt;

&lt;p&gt;If you’ve built anything with Turbo and Stimulus beyond basic forms, you’ve likely seen this. Components work fine on full page loads, but Turbo introduces a different lifecycle. Pages get cached mid-state, morphs overwrite client-side changes with stale server HTML, and your UI ends up stuck in states it should have left behind.&lt;/p&gt;

&lt;p&gt;Fixing this in &lt;a href=&quot;/documentation/components/&quot;&gt;Maquina Components&lt;/a&gt; is what version 0.4.0 is about.&lt;/p&gt;

&lt;h2 id=&quot;the-teardown-pattern&quot;&gt;The Teardown Pattern&lt;/h2&gt;

&lt;p&gt;The core problem is described well by &lt;a href=&quot;https://betterstimulus.com/turbo/teardown&quot;&gt;Better Stimulus&lt;/a&gt;. When Turbo navigates away from a page, it takes a snapshot of the DOM before leaving. When the user returns, Turbo shows that snapshot first. Any DOM changes your Stimulus controllers made—open menus, expanded panels, loading classes—get frozen into the cache.&lt;/p&gt;

&lt;p&gt;The standard Stimulus &lt;code class=&quot;highlighter-rouge&quot;&gt;disconnect&lt;/code&gt; callback handles general cleanup, but it doesn’t distinguish between “the element was removed from the DOM” and “Turbo is about to cache this page.” You need both.&lt;/p&gt;

&lt;p&gt;The Teardown pattern adds a &lt;code class=&quot;highlighter-rouge&quot;&gt;teardown&lt;/code&gt; method to controllers, triggered by Turbo’s &lt;code class=&quot;highlighter-rouge&quot;&gt;turbo:before-cache&lt;/code&gt; event. Every controller that manipulates the DOM can opt in, resetting its visual state before Turbo takes the snapshot. This keeps &lt;code class=&quot;highlighter-rouge&quot;&gt;disconnect&lt;/code&gt; clean for general lifecycle concerns and gives Turbo-specific rollback its own dedicated path.&lt;/p&gt;

&lt;p&gt;This release applies that pattern across the interactive components in the library.&lt;/p&gt;

&lt;h2 id=&quot;sidebar-three-problems-at-once&quot;&gt;Sidebar: Three Problems at Once&lt;/h2&gt;

&lt;p&gt;The sidebar was the hardest to get right. It had three separate issues interacting with each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Random IDs broke morphing.&lt;/strong&gt; The sidebar generated IDs like &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-a3f9b2&lt;/code&gt; on every render. Turbo’s idiomorph algorithm matches elements by ID—when the ID changes every time, idiomorph can’t find the element and treats it as new. Every morph was destroying and recreating the sidebar from scratch. The fix: deterministic IDs. &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-left&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-right&lt;/code&gt;, consistent across renders. The sidebar provider also gets a stable ID (&lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-provider&lt;/code&gt; by default).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Morphs overwrote client state.&lt;/strong&gt; The sidebar stores its open/closed state in a cookie so it persists across page loads. During a Turbo morph, the server sends back HTML with the default state—it doesn’t know about the cookie. Idiomorph applies the server HTML, and the sidebar collapses even though the user had it open.&lt;/p&gt;

&lt;p&gt;The fix adds a &lt;code class=&quot;highlighter-rouge&quot;&gt;turbo:before-morph-element&lt;/code&gt; listener with a &lt;code class=&quot;highlighter-rouge&quot;&gt;_morphing&lt;/code&gt; guard flag. When a morph happens, the controller reads the cookie (the source of truth on the client), reasserts the correct state, and strips the &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-loading&lt;/code&gt; class that the server HTML reintroduces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout shift on desktop.&lt;/strong&gt; When Stimulus initialized and switched the sidebar from its mobile offcanvas mode to the desktop collapsible mode, there was a visible jump. The transition happened after the browser had already painted. This release smooths that handoff so the mode switch doesn’t cause a flash.&lt;/p&gt;

&lt;h2 id=&quot;the-yield-trap&quot;&gt;The Yield Trap&lt;/h2&gt;

&lt;p&gt;The second category of fixes has nothing to do with Turbo. It’s a Rails rendering behavior that caught me off guard.&lt;/p&gt;

&lt;p&gt;Nine partials in the library used the standard block pattern:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card/description&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Custom HTML&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works when you always pass a block. But render the partial &lt;em&gt;without&lt;/em&gt; a block and &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; inside it doesn’t return nothing—it renders the entire page’s content into the partial. Rails treats the missing block as a signal to yield the page-level content instead.&lt;/p&gt;

&lt;p&gt;The result: components rendering the full page body inside a card title or a toast message. It only shows up in specific usage patterns, and when it does, the output looks completely wrong with no obvious cause.&lt;/p&gt;

&lt;p&gt;The fix replaces &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; with an explicit &lt;code class=&quot;highlighter-rouge&quot;&gt;content:&lt;/code&gt; parameter in all nine affected partials:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;card/title, card/description&lt;/li&gt;
  &lt;li&gt;alert/title, alert/description&lt;/li&gt;
  &lt;li&gt;toast/title, toast/description&lt;/li&gt;
  &lt;li&gt;combobox/label, toast (main), toaster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The five toast helper methods no longer accept blocks either.&lt;/p&gt;

&lt;h2 id=&quot;breaking-changes&quot;&gt;Breaking Changes&lt;/h2&gt;

&lt;p&gt;This is a minor version bump with breaking changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Block syntax removed&lt;/strong&gt; for the 9 partials listed above. Use &lt;code class=&quot;highlighter-rouge&quot;&gt;content: capture { ... }&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;do ... end&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Toast helpers&lt;/strong&gt; no longer accept blocks. Use the &lt;code class=&quot;highlighter-rouge&quot;&gt;content:&lt;/code&gt; parameter.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sidebar IDs&lt;/strong&gt; changed from &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-&amp;lt;random_hex&amp;gt;&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-left&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-right&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sidebar provider&lt;/strong&gt; now has a stable &lt;code class=&quot;highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute (&lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-provider&lt;/code&gt; by default).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;migration&quot;&gt;Migration&lt;/h2&gt;

&lt;p&gt;The content parameter change is mechanical. Find every block-style call to the affected partials and wrap the content with &lt;code class=&quot;highlighter-rouge&quot;&gt;capture&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card/description&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Custom HTML&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card/description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;content: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Custom HTML&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For sidebar IDs, if you reference specific sidebar element IDs in JavaScript or tests, update them to &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-left&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;sidebar-right&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;/h2&gt;

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

&lt;h2 id=&quot;what-this-reinforced&quot;&gt;What This Reinforced&lt;/h2&gt;

&lt;p&gt;Turbo is not a transparent layer over page loads. It’s a different execution model. Any Stimulus controller that touches the DOM needs to account for caching, morphing, and the gap between what the server renders and what the client has changed since. The &lt;a href=&quot;https://betterstimulus.com/turbo/teardown&quot;&gt;Teardown pattern&lt;/a&gt; should be the default starting point for any controller that does more than read values.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; behavior in Rails partials was a genuine surprise. It’s documented, but it’s a quiet trap when you have optional block content. Explicit parameters are safer.&lt;/p&gt;

&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://maquina.app/documentation/components/&quot;&gt;Component Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://maquina.app/documentation/components/sidebar/&quot;&gt;Sidebar&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://maquina.app/documentation/components/card/&quot;&gt;Card&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://maquina.app/documentation/components/alert/&quot;&gt;Alert&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://maquina.app/documentation/components/toast/&quot;&gt;Toast&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://maquina.app/documentation/components/combobox/&quot;&gt;Combobox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;source&quot;&gt;Source&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/maquina_components&quot;&gt;Maquina Components&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/maquina_components/compare/v0.3.1.1...v0.4.0&quot;&gt;Full Changelog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://betterstimulus.com/turbo/teardown&quot;&gt;Better Stimulus: Teardown Pattern&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-announce-maquina-components.png" /><media:content medium="image" url="https://maquina.app/images/og-blog-announce-maquina-components.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Maquina 0.3.1: Calendar, Date Picker &amp;amp; Claude Code Skills</title><link href="https://maquina.app/blog/2026/01/maquina-0-3-1-calendar-date-picker-claude-skills/" rel="alternate" type="text/html" title="Maquina 0.3.1: Calendar, Date Picker &amp; Claude Code Skills" /><published>2026-01-23T00:00:00-06:00</published><updated>2026-01-23T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-01-23-maquina-0-3-1-calendar-date-picker-claude-skills.md</id><content type="html" xml:base="https://maquina.app/blog/2026/01/maquina-0-3-1-calendar-date-picker-claude-skills/">&lt;p&gt;This month brings updates across the Maquina ecosystem: new Calendar and Date Picker components for Rails, two Claude Code skills for AI-assisted development, and live interactive previews for all components in the documentation.&lt;/p&gt;

&lt;h2 id=&quot;maquina-components-031&quot;&gt;Maquina Components 0.3.1&lt;/h2&gt;

&lt;p&gt;Building on &lt;a href=&quot;/blog/2026/01/maquina-components-0.3.0-combobox-and-toast/&quot;&gt;version 0.3.0&lt;/a&gt;, this release adds two components for date selection: &lt;strong&gt;Calendar&lt;/strong&gt; and &lt;strong&gt;Date Picker&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;calendar&quot;&gt;Calendar&lt;/h3&gt;

&lt;p&gt;An inline calendar for single date or range selection. Useful when you need the full calendar visible—booking flows, availability displays, or any context where date proximity matters.&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/calendar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;mode: :range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;selected: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;selected_end: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For form integration, the calendar generates hidden inputs automatically:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form_with&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;model: &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@booking&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/calendar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;mode: :range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;input_name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;booking[check_in]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;input_name_end: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;booking[check_out]&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt; Single or range selection, min/max date constraints, disabled dates, week start configuration, and direct form integration with hidden inputs.&lt;/p&gt;

&lt;h3 id=&quot;date-picker&quot;&gt;Date Picker&lt;/h3&gt;

&lt;p&gt;A button that opens a calendar in a popover. Better for forms where space is limited and you don’t need the calendar always visible.&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/date_picker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;mode: :single&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;placeholder: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Select a date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;input_name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;event[date]&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Range selection works the same way:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/date_picker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;mode: :range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;placeholder: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Select date range&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;input_name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;start_date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;input_name_end: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;end_date&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt; Single or range selection, pre-selected date display, min/max boundaries, disabled state, and customizable placeholders.&lt;/p&gt;

&lt;h3 id=&quot;when-to-use-which&quot;&gt;When to Use Which&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Use Case&lt;/th&gt;
      &lt;th&gt;Component&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Booking calendar with visible availability&lt;/td&gt;
      &lt;td&gt;Calendar&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Date field in a form&lt;/td&gt;
      &lt;td&gt;Date Picker&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Date range with context (prices, events)&lt;/td&gt;
      &lt;td&gt;Calendar&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Quick date selection in limited space&lt;/td&gt;
      &lt;td&gt;Date Picker&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;live-previews&quot;&gt;Live Previews&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/components/components-green.png&quot; alt=&quot;Maquina Components live previews showing components in green theme&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The documentation site now includes live, interactive previews for all components. Visit any component page in the &lt;a href=&quot;/documentation/components/&quot;&gt;documentation&lt;/a&gt; to see working examples in light and dark themes, multiple color variations, and code ready to copy.&lt;/p&gt;

&lt;p&gt;For a complete showcase, the &lt;a href=&quot;https://demo.maquina.app&quot;&gt;live demo application&lt;/a&gt; shows all components working together with sample data.&lt;/p&gt;

&lt;h3 id=&quot;upgrading&quot;&gt;Upgrading&lt;/h3&gt;

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

&lt;p&gt;No generator changes required for existing installations.&lt;/p&gt;

&lt;h2 id=&quot;claude-code-skills&quot;&gt;Claude Code Skills&lt;/h2&gt;

&lt;p&gt;Two new skills for AI-assisted Rails development.&lt;/p&gt;

&lt;h3 id=&quot;maquina-ui-standards&quot;&gt;Maquina UI Standards&lt;/h3&gt;

&lt;p&gt;Teaches Claude how to build UIs with &lt;code class=&quot;highlighter-rouge&quot;&gt;maquina_components&lt;/code&gt;. Without guidance, Claude generates generic Rails patterns—plain divs, inline styles, inconsistent markup. With this skill, Claude generates code using your actual component library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Includes:&lt;/strong&gt; Component catalog with 20+ components, form patterns, layout patterns, Turbo integration, and accessibility guidelines.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/plugin marketplace add maquina-app/rails-claude-code
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;maquina-ui-standards@maquina
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ask Claude &lt;em&gt;“Create a users index view with a table”&lt;/em&gt; and get:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card/header&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card/title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;text: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Users&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/card/content&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/table&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head_cell&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Name&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head_cell&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Email&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Documentation: &lt;a href=&quot;/documentation/ai-tools/maquina-ui-standards/&quot;&gt;Maquina UI Standards&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;rails-simplifier&quot;&gt;Rails Simplifier&lt;/h3&gt;

&lt;p&gt;Refines Rails code following 37signals patterns and the One Person Framework philosophy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Converts service objects to model methods, transforms custom actions to CRUD resources, moves logic from controllers to models, detects N+1 queries, and applies Rails conventions like I18n and Time.current.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/plugin marketplace add maquina-app/rails-claude-code
/plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;rails-simplifier@maquina
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Example prompts:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; Review recent changes using the rails-simplifier agent
&amp;gt; Use rails-simplifier to review the bookings controller
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Documentation: &lt;a href=&quot;/documentation/ai-tools/rails-simplifier/&quot;&gt;Rails Simplifier&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;source&quot;&gt;Source&lt;/h2&gt;

&lt;p&gt;All projects are MIT licensed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/maquina_components&quot;&gt;Maquina Components&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/rails-claude-code&quot;&gt;Rails Claude Code Skills&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-announce-maquina-components.png" /><media:content medium="image" url="https://maquina.app/images/og-blog-announce-maquina-components.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Claude Skill for Maquina Components</title><link href="https://maquina.app/blog/2026/01/claude-skill-for-maquina-components/" rel="alternate" type="text/html" title="Claude Skill for Maquina Components" /><published>2026-01-08T00:00:00-06:00</published><updated>2026-01-08T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-01-08-maquina-ui-standards-skill.md</id><content type="html" xml:base="https://maquina.app/blog/2026/01/claude-skill-for-maquina-components/">&lt;p&gt;When I started extracting and standardizing &lt;a href=&quot;/blog/announcing-maquina-components/&quot;&gt;maquina_components&lt;/a&gt; from real production applications, I was also experimenting with AI-assisted development. The two efforts ran in parallel—building a consistent component library while trying to get Claude to help me use it.&lt;/p&gt;

&lt;p&gt;The results were mixed. Every time I asked Claude to build a view or implement a form, it was back and forth. “Use the card partial, not a div.” “The input needs a data attribute.” “That’s not how the combobox works.” I spent as much time correcting the AI as I would have spent writing the code myself.&lt;/p&gt;

&lt;p&gt;The same friction appeared when writing specs. I’d describe a feature and Claude would suggest generic Rails patterns instead of the components I had available. It didn’t know about the library. How could it?&lt;/p&gt;

&lt;h2 id=&quot;the-skill-experiment&quot;&gt;The Skill Experiment&lt;/h2&gt;

&lt;p&gt;When Anthropic released the &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/skills&quot;&gt;Skills functionality&lt;/a&gt;, I wondered if it was the right tool for this problem. Skills let you teach Claude project-specific knowledge—conventions, patterns, APIs. Exactly what was missing.&lt;/p&gt;

&lt;p&gt;I created a first version: a structured reference with component examples, form patterns, layout conventions, and Turbo integration guides. Added it to my projects and started using it.&lt;/p&gt;

&lt;p&gt;It worked. Claude started generating code that matched my conventions. The combobox had proper keyboard navigation. Forms used the right data attributes. Turbo Streams updated components correctly. The back-and-forth dropped significantly.&lt;/p&gt;

&lt;p&gt;I kept the skill private. It was tied to my workflow, my projects. Not ready for others.&lt;/p&gt;

&lt;h2 id=&quot;making-it-public&quot;&gt;Making It Public&lt;/h2&gt;

&lt;p&gt;Yesterday I published &lt;a href=&quot;/blog/maquina-components-0-3-0/&quot;&gt;Maquina Components 0.3.0&lt;/a&gt; with Combobox and Toast. Shortly after, someone asked if I had an MCP server for the components.&lt;/p&gt;

&lt;p&gt;I replied that I had something better—a Claude Skill that I’d been using for while now. It was working great with the gem.&lt;/p&gt;

&lt;p&gt;So I decided to open source it.&lt;/p&gt;

&lt;h2 id=&quot;what-the-skill-provides&quot;&gt;What the Skill Provides&lt;/h2&gt;

&lt;p&gt;A complete reference for building UIs with maquina_components:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Reference&lt;/th&gt;
      &lt;th&gt;Purpose&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Component catalog&lt;/td&gt;
      &lt;td&gt;All 15+ components with ERB examples&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Form patterns&lt;/td&gt;
      &lt;td&gt;Validation, error handling, inline layouts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Layout patterns&lt;/td&gt;
      &lt;td&gt;Sidebar navigation, page structure&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Turbo integration&lt;/td&gt;
      &lt;td&gt;Frames, Streams, component updates&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Spec checklist&lt;/td&gt;
      &lt;td&gt;Review criteria for UI quality&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;p&gt;Create a skills directory in your Rails project:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;your-rails-app
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; .claude/skills
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Download the skill from the &lt;a href=&quot;https://github.com/maquina-app/maquina_components/tree/main/skill&quot;&gt;maquina_components repository&lt;/a&gt; and copy it to &lt;code class=&quot;highlighter-rouge&quot;&gt;.claude/skills/maquina-ui-standards/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then update your &lt;code class=&quot;highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; to reference it:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gu&quot;&gt;## UI Components&lt;/span&gt;

This project uses maquina_components for UI. Before implementing views,
forms, or interactive components, read the UI standards skill:

.claude/skills/maquina-ui-standards/SKILL.md

Always consult the skill when:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Creating or modifying views
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Implementing forms
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Adding interactive components
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Building layouts with sidebar/header patterns
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Working with Turbo Streams that update UI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;

&lt;p&gt;Once installed, ask Claude naturally:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Create the users index view with a table showing name, email, and status.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Implement the project form with name, description, and a framework combobox.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Review this view against the maquina UI standards and suggest improvements.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The generated code matches what you’d write manually—just faster, and without the back-and-forth.&lt;/p&gt;

&lt;h2 id=&quot;source&quot;&gt;Source&lt;/h2&gt;

&lt;p&gt;The skill is included in the &lt;a href=&quot;https://github.com/maquina-app/maquina_components/tree/main/skill&quot;&gt;maquina_components repository&lt;/a&gt; under MIT license. Updates follow gem releases.&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-claude-skill.png" /><media:content medium="image" url="https://maquina.app/images/og-blog-claude-skill.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Maquina Components 0.3.0: Combobox and Toast</title><link href="https://maquina.app/blog/2026/01/maquina-components-0.3.0-combobox-and-toast/" rel="alternate" type="text/html" title="Maquina Components 0.3.0: Combobox and Toast" /><published>2026-01-07T00:00:00-06:00</published><updated>2026-01-07T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2026-01-07-maquina-components-0-3-0.md</id><content type="html" xml:base="https://maquina.app/blog/2026/01/maquina-components-0.3.0-combobox-and-toast/">&lt;p&gt;Version 0.3.0 of &lt;a href=&quot;/blog/2025/12/announcing-maquina-components-opinionated-ul-for-rails-applications/&quot;&gt;Maquina Components&lt;/a&gt; adds two frequently requested interactive components: &lt;strong&gt;Combobox&lt;/strong&gt; and &lt;strong&gt;Toast&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Both components follow the same philosophy as the rest of the library—ERB partials, Tailwind CSS, and Stimulus controllers only where necessary.&lt;/p&gt;

&lt;h2 id=&quot;combobox&quot;&gt;Combobox&lt;/h2&gt;

&lt;p&gt;&lt;img alt=&quot;Combobox&quot; src=&quot;/images/components/combobox.png&quot; class=&quot;mb-10 w-full rounded-2xl object-cover&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An autocomplete input with a searchable dropdown list. Useful when selecting from many options—countries, users, tags, or any list that benefits from filtering.&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combobox&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;placeholder: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Select framework...&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trigger&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;placeholder: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Search...&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rails&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;Ruby on Rails&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;django&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;Django&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;phoenix&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;Phoenix&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For simpler use cases, the data-driven helper builds the entire structure from an array:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combobox_simple&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;placeholder: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Select country...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;user[country]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;options: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Country&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Keyboard navigation (arrows, Home, End, Escape)&lt;/li&gt;
  &lt;li&gt;Real-time filtering as you type&lt;/li&gt;
  &lt;li&gt;Grouped options with labels and separators&lt;/li&gt;
  &lt;li&gt;Multiple width and alignment options&lt;/li&gt;
  &lt;li&gt;Full ARIA support (&lt;code class=&quot;highlighter-rouge&quot;&gt;role=&quot;combobox&quot;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;role=&quot;listbox&quot;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The Combobox uses the HTML5 Popover API for light-dismiss behavior. Most modern browsers support it natively:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Browser&lt;/th&gt;
      &lt;th&gt;Version&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Chrome&lt;/td&gt;
      &lt;td&gt;114+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Edge&lt;/td&gt;
      &lt;td&gt;114+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Safari&lt;/td&gt;
      &lt;td&gt;17+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Firefox&lt;/td&gt;
      &lt;td&gt;125+&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;For older browsers, add the &lt;a href=&quot;https://github.com/oddbird/popover-polyfill&quot;&gt;popover polyfill&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @oddbird/popover-polyfill
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/javascript/application.js&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@oddbird/popover-polyfill&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;toast&quot;&gt;Toast&lt;/h2&gt;

&lt;p&gt;&lt;img alt=&quot;Combobox&quot; src=&quot;/images/components/toast.png&quot; class=&quot;mb-10 w-full rounded-2xl object-cover&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Non-intrusive notifications that appear temporarily and dismiss automatically. Ideal for form submission feedback, background task completion, or any transient message.&lt;/p&gt;

&lt;h3 id=&quot;server-side-with-flash-messages&quot;&gt;Server-Side with Flash Messages&lt;/h3&gt;

&lt;p&gt;The most common pattern—render Rails flash messages as toasts:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;components/toaster&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;position: :bottom_right&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toast_flash_messages&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# In your controller&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Profile updated successfully!&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Flash types map automatically to toast variants: &lt;code class=&quot;highlighter-rouge&quot;&gt;:success&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;:error&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;:warning&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;:info&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;javascript-api&quot;&gt;JavaScript API&lt;/h3&gt;

&lt;p&gt;For dynamic notifications without a page reload:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Changes saved!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Connection lost&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Please check your internet connection.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;warning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Session expiring&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Dismiss programmatically&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Processing...&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;with-turbo-streams&quot;&gt;With Turbo Streams&lt;/h3&gt;

&lt;p&gt;Append toasts to the container in Turbo Stream responses:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turbo_stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;toaster&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toast&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Post published!&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;ul&gt;
  &lt;li&gt;Five variants: default, success, info, warning, error&lt;/li&gt;
  &lt;li&gt;Auto-dismiss with configurable duration (pauses on hover)&lt;/li&gt;
  &lt;li&gt;Six positioning options (corners and center edges)&lt;/li&gt;
  &lt;li&gt;Optional action buttons for undo/view operations&lt;/li&gt;
  &lt;li&gt;Full keyboard accessibility&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Toast requires Stimulus for the auto-dismiss timer and JavaScript API. Add the controller to your Stimulus application:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/javascript/application.js&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eagerLoadControllersFrom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@hotwired/stimulus-loading&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;eagerLoadControllersFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;controllers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;/h2&gt;

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

&lt;p&gt;No generator changes are required. Both components use the existing theme variables.&lt;/p&gt;

&lt;h2 id=&quot;see-them-in-action&quot;&gt;See Them in Action&lt;/h2&gt;

&lt;p&gt;To explore Combobox, Toast, and all other components with demo data, clone the repository and run the dummy application:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/maquina-app/maquina_components.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;maquina_components/test/dummy
bin/dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then visit &lt;a href=&quot;http://localhost:5300&quot;&gt;http://localhost:5300&lt;/a&gt; to interact with the full component showcase.&lt;/p&gt;

&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/documentation/components/combobox/&quot;&gt;Combobox documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/documentation/components/toast/&quot;&gt;Toast documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/documentation/components/&quot;&gt;Full component list&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;source&quot;&gt;Source&lt;/h2&gt;

&lt;p&gt;The gem is MIT licensed. Source and issues on &lt;a href=&quot;https://github.com/maquina-app/maquina_components&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-announce-maquina-components.png" /><media:content medium="image" url="https://maquina.app/images/og-blog-announce-maquina-components.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Rails MCP Server 1.5.0: Security Hardening and Sandboxed Environment Support</title><link href="https://maquina.app/blog/2025/12/rails-mcp-server-1-5-0-security-hardening/" rel="alternate" type="text/html" title="Rails MCP Server 1.5.0: Security Hardening and Sandboxed Environment Support" /><published>2025-12-29T00:00:00-06:00</published><updated>2025-12-29T00:00:00-06:00</updated><id>repo://posts.collection/_posts/2025-12-29-rails-mcp-1-5.md</id><content type="html" xml:base="https://maquina.app/blog/2025/12/rails-mcp-server-1-5-0-security-hardening/">&lt;p&gt;Open source projects get better when people contribute back. Rails MCP Server 1.5.0 is a direct result of that—a release shaped significantly by a community contribution that I didn’t write.&lt;/p&gt;

&lt;p&gt;The most important change in this version is a comprehensive security overhaul contributed by GitHub user &lt;a href=&quot;https://github.com/hellvinz&quot;&gt;hellvinz&lt;/a&gt; through &lt;a href=&quot;https://github.com/maquina-app/rails-mcp-server/pull/25&quot;&gt;PR #25&lt;/a&gt;. It’s the kind of work that doesn’t get enough recognition.&lt;/p&gt;

&lt;h2 id=&quot;the-security-contribution&quot;&gt;The Security Contribution&lt;/h2&gt;

&lt;p&gt;When you give an AI model access to your codebase through MCP tools, security matters. The &lt;code class=&quot;highlighter-rouge&quot;&gt;execute_ruby&lt;/code&gt; sandbox already restricted dangerous operations, but the file-accessing analyzers needed more rigorous input validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PathValidator Module&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A centralized validation layer now protects all file-accessing analyzers. Path traversal attempts are blocked. Sensitive files are filtered automatically. The implementation is clean:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Path traversal attempts are blocked&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;path: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;../../../etc/passwd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;Access denied: Path is outside the project directory&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Sensitive files are filtered&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;list_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;pattern: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;config/*.key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; master.key, credentials.yml.enc excluded from results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The validator catches:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Path traversal attacks (&lt;code class=&quot;highlighter-rouge&quot;&gt;../&lt;/code&gt; sequences)&lt;/li&gt;
  &lt;li&gt;Absolute path access outside the project&lt;/li&gt;
  &lt;li&gt;Attempts to read sensitive files (master.key, credentials.yml.enc, .env)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Injection Prevention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Shell commands now use &lt;code class=&quot;highlighter-rouge&quot;&gt;IO.popen&lt;/code&gt; with array arguments instead of string interpolation. Table names in schema queries are validated against a strict pattern. These changes close potential injection vectors that existed in earlier versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Beyond the code changes, hellvinz added security infrastructure I should have set up from the start:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Dependabot for dependency updates&lt;/li&gt;
  &lt;li&gt;CodeQL for static analysis&lt;/li&gt;
  &lt;li&gt;OpenSSF Scorecard integration&lt;/li&gt;
  &lt;li&gt;A proper SECURITY.md for vulnerability reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This kind of contribution takes real effort. Reviewing an unfamiliar codebase, identifying gaps, implementing fixes that don’t break existing functionality, unglamorous work that makes the project better for everyone who uses it.&lt;/p&gt;

&lt;p&gt;I’m grateful for the contribution.&lt;/p&gt;

&lt;h2 id=&quot;sandboxed-environment-support&quot;&gt;Sandboxed Environment Support&lt;/h2&gt;

&lt;p&gt;AI coding agents increasingly run in sandboxed environments—containers or restricted shells where they can only access the current project directory. GitHub Copilot Agent and Claude Code Agent both work this way.&lt;/p&gt;

&lt;p&gt;Previous versions of Rails MCP Server assumed access to a user home directory for configuration files. That doesn’t work in a sandbox.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;--single-project&lt;/code&gt; flag solves this. It tells the server to use the current working directory as the only project, skipping configuration files entirely:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails-mcp-server &lt;span class=&quot;nt&quot;&gt;--single-project&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot Agent&lt;/strong&gt; configuration goes in &lt;code class=&quot;highlighter-rouge&quot;&gt;.vscode/mcp.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;servers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;rails-mcp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rails-mcp-server&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--single-project&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Claude Code Agent&lt;/strong&gt; can use the same flag. The server detects it’s running in a Rails directory and works immediately—no setup required.&lt;/p&gt;

&lt;p&gt;This also simplifies CI/CD pipelines and any environment where you want the server to just work with the current directory.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/maquina-app/rails-mcp-server/blob/main/docs/COPILOT_AGENT.md&quot;&gt;Copilot Agent documentation&lt;/a&gt; covers the setup in detail.&lt;/p&gt;

&lt;h2 id=&quot;simplified-project-configuration&quot;&gt;Simplified Project Configuration&lt;/h2&gt;

&lt;p&gt;Previous versions required manual configuration in &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.config/rails-mcp/projects.yml&lt;/code&gt;. That still works, but 1.5.0 adds flexibility:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Use Case&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;--single-project&lt;/code&gt; flag&lt;/td&gt;
      &lt;td&gt;Sandboxed agents (Copilot, Claude Code), CI/CD&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RAILS_MCP_PROJECT_PATH&lt;/code&gt; env var&lt;/td&gt;
      &lt;td&gt;Explicit path control&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Auto-detection&lt;/td&gt;
      &lt;td&gt;Finds Rails apps from Gemfile, engines from gemspec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;projects.yml&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Multiple projects with named references&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The server now auto-detects Rails applications by checking for a Gemfile with the &lt;code class=&quot;highlighter-rouge&quot;&gt;rails&lt;/code&gt; gem, and Rails engines by looking for gemspec files with Rails dependencies. When only one project is available, it switches automatically.&lt;/p&gt;

&lt;h2 id=&quot;rails-81-compatibility&quot;&gt;Rails 8.1 Compatibility&lt;/h2&gt;

&lt;p&gt;Rails 8.1 changed the internal callback API. The &lt;code class=&quot;highlighter-rouge&quot;&gt;analyze_controller_views&lt;/code&gt; tool was calling &lt;code class=&quot;highlighter-rouge&quot;&gt;callback.options&lt;/code&gt; to extract &lt;code class=&quot;highlighter-rouge&quot;&gt;:only&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;:except&lt;/code&gt; conditions, but that method no longer exists.&lt;/p&gt;

&lt;p&gt;The fix maintains backward compatibility:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;ss&quot;&gt;callbacks: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_process_action_callbacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;kind: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;filter: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:only&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:only&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works with Rails 6.0 through 8.1. The callback conditions are extracted when available, omitted when not.&lt;/p&gt;

&lt;h2 id=&quot;other-changes&quot;&gt;Other Changes&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Error messages&lt;/strong&gt; now include hints. When you ask for a model named &lt;code class=&quot;highlighter-rouge&quot;&gt;users&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;User&lt;/code&gt;, the error explains the naming convention. Small things that reduce friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter passing&lt;/strong&gt; in &lt;code class=&quot;highlighter-rouge&quot;&gt;execute_tool&lt;/code&gt; is fixed. The params schema now generates correctly for MCP clients, so tools like &lt;code class=&quot;highlighter-rouge&quot;&gt;analyze_models&lt;/code&gt; can actually receive their parameters. This was a real bug that made the tool harder to use than it should have been.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input validation&lt;/strong&gt; for &lt;code class=&quot;highlighter-rouge&quot;&gt;load_guide&lt;/code&gt; prevents path traversal in guide names. Another gap that hellvinz’s security review prompted me to address.&lt;/p&gt;

&lt;h2 id=&quot;breaking-change&quot;&gt;Breaking Change&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;load_guide&lt;/code&gt; analyzer renamed its parameter from &lt;code class=&quot;highlighter-rouge&quot;&gt;guides&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;library&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before (1.4.x)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;execute_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;load_guide&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;guides: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rails&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;guide: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;active_record&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After (1.5.0)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;execute_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;load_guide&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;library: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rails&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;guide: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;active_record&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The change clarifies that you’re selecting a documentation library (rails, turbo, stimulus, kamal, custom), not multiple guides. It’s a small breaking change, but the naming is more accurate.&lt;/p&gt;

&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem update rails-mcp-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re using Claude Desktop, restart it to pick up the new version. The server binary path in your configuration doesn’t change.&lt;/p&gt;

&lt;p&gt;For new installations:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;rails-mcp-server
rails-mcp-config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The interactive configuration tool handles Claude Desktop setup, project registration, and guide downloads.&lt;/p&gt;

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

&lt;p&gt;The MCP specification continues to evolve. As more AI tools adopt the protocol, Rails MCP Server will adapt to support them.&lt;/p&gt;

&lt;p&gt;If you find issues or have ideas, the &lt;a href=&quot;https://github.com/maquina-app/rails-mcp-server/issues&quot;&gt;issue tracker&lt;/a&gt; is open. Pull requests are welcome. As this release shows, community contributions make a real difference—sometimes more than you might expect.&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/rails-mcp-server&quot;&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/rails-mcp-server&quot;&gt;RubyGems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/documentation/ai-tools/rails-mcp-server/&quot;&gt;Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/rails-mcp-server/blob/main/docs/AGENT.md&quot;&gt;AI Agent Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/maquina-app/rails-mcp-server/blob/main/docs/COPILOT_AGENT.md&quot;&gt;Copilot Agent Setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Mario Alberto Chávez Cárdenas</name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://maquina.app/images/og-blog-rails-mcp-server-1-5-0.png" /><media:content medium="image" url="https://maquina.app/images/og-blog-rails-mcp-server-1-5-0.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>