<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Juan Ibiapina</title><description>Juan Ibiapina&apos;s blog on agentic coding, developer tooling, and the things that catch his attention.</description><link>https://juanibiapina.dev/</link><language>en</language><atom:link href="https://juanibiapina.dev/rss.xml" rel="self" type="application/rss+xml"/><item><title>Ask When Uncertain</title><link>https://juanibiapina.dev/posts/2026-10-09-ask-when-uncertain/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-10-09-ask-when-uncertain/</guid><description>Why I doubt &quot;ask when uncertain&quot; works in AI prompts.</description><pubDate>Fri, 09 Oct 2026 12:15:04 GMT</pubDate><content:encoded>&lt;p&gt;&quot;Ask when uncertain&quot;. I&apos;ve seen that in AI prompts.&lt;/p&gt;
&lt;p&gt;I doubt that works. Of course one would have to measure to know if it works, but measure what? We could measure if it asked more questions. We could also use a set of benchmarks where we expect it to stop at an exact place.&lt;/p&gt;
&lt;p&gt;But I wouldn&apos;t expect two different humans to act on uncertainty the same way, why would I expect two different models to act on uncertainty the same way? It just seems unreliable – too dependent on training.&lt;/p&gt;
&lt;p&gt;How about instead we try to be precise in specifying outputs?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explore three different options and weigh pros and cons in terms of &amp;#x3C;actual list of criteria&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That way we control the output. I expect three options. I expect pros and cons for each item in the list of criteria.&lt;/p&gt;
&lt;h2&gt;Interesting Articles&lt;/h2&gt;
&lt;p&gt;Su, J., &amp;#x26; Cardie, C. (2026). &lt;em&gt;Knowing but not showing: LLMs recognize ambiguity but rarely ask clarifying questions&lt;/em&gt; [Preprint]. arXiv. &lt;a href=&quot;https://doi.org/10.48550/arXiv.2605.25284&quot;&gt;https://doi.org/10.48550/arXiv.2605.25284&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We find a clear gap between recognition and behavior: models often identify ambiguity when explicitly asked to judge it, yet in the QA setting they overwhelmingly default to direct answers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wang, W., Juluan, S., Ling, Z., Chan, Y.-K., Wang, C., Lee, C., Yuan, Y., Huang, J.-t., Jiao, W., &amp;#x26; Lyu, M. R. (2025). Learning to ask: When LLM agents meet unclear instruction. In C. Christodoulopoulos, T. Chakraborty, C. Rose, &amp;#x26; V. Peng (Eds.), &lt;em&gt;Proceedings of the 2025 Conference on Empirical Methods in Natural Language Processing&lt;/em&gt; (pp. 21773–21784). Association for Computational Linguistics. &lt;a href=&quot;https://doi.org/10.18653/v1/2025.emnlp-main.1104&quot;&gt;https://doi.org/10.18653/v1/2025.emnlp-main.1104&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We find that due to the next-token prediction training objective, LLM agents tend to arbitrarily generate the missed argument, which may lead to hallucinations and risks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Xiong, M., Hu, Z., Lu, X., Li, Y., Fu, J., He, J., &amp;#x26; Hooi, B. (2024). Can LLMs express their uncertainty? An empirical evaluation of confidence elicitation in LLMs. In &lt;em&gt;The Twelfth International Conference on Learning Representations&lt;/em&gt;. &lt;a href=&quot;https://proceedings.iclr.cc/paper_files/paper/2024/hash/6733cf15e10e2cd1d59af033c3bb8507-Abstract-Conference.html&quot;&gt;https://proceedings.iclr.cc/paper_files/paper/2024/hash/6733cf15e10e2cd1d59af033c3bb8507-Abstract-Conference.html&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LLMs, when verbalizing their confidence, tend to be overconfident, potentially imitating human patterns of expressing confidence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yang, D., Tsai, Y.-H. H., &amp;#x26; Yamada, M. (2024). &lt;em&gt;On verbalized confidence scores for LLMs&lt;/em&gt; [Preprint]. arXiv. &lt;a href=&quot;https://doi.org/10.48550/arXiv.2412.14737&quot;&gt;https://doi.org/10.48550/arXiv.2412.14737&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our results reveal that the reliability of these scores strongly depends on how the model is asked, but also that it is possible to extract well-calibrated confidence scores with certain prompt methods.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Zhang, M. J. Q., Knox, W. B., &amp;#x26; Choi, E. (2025). Modeling future conversation turns to teach LLMs to ask clarifying questions. In &lt;em&gt;The Thirteenth International Conference on Learning Representations&lt;/em&gt;. &lt;a href=&quot;https://proceedings.iclr.cc/paper_files/paper/2025/hash/97e2df4bb8b2f1913657344a693166a2-Abstract-Conference.html&quot;&gt;https://proceedings.iclr.cc/paper_files/paper/2025/hash/97e2df4bb8b2f1913657344a693166a2-Abstract-Conference.html&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Existing LLMs often respond by presupposing a single interpretation of such ambiguous requests, frustrating users who intended a different interpretation.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>ai</category><category>blog</category></item><item><title>A Beautiful Machine to Watch</title><link>https://juanibiapina.dev/posts/2026-06-23-developing-deltoids/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-06-23-developing-deltoids/</guid><description>Building Deltoids with Claude: vibe-slop, edge cases, legacy code, and the algorithm I had to feel my way toward. The tools changed; the feeling didn&apos;t.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When developing &lt;a href=&quot;https://deltoids.dev&quot;&gt;deltoids&lt;/a&gt;, I started with a problem I needed to solve. I wanted to &lt;strong&gt;see more context when reviewing code&lt;/strong&gt;, and I wanted that context to be smart. Code is structured, but diffs only show a couple of lines before and after the change. I asked Claude to implement it, and the initial version was not well designed. You could call it vibe-slop, in the sense that I didn&apos;t properly design or review it.&lt;/p&gt;
&lt;p&gt;As I used it, edge cases started to appear, and some were bad. Parts of the diff went missing. Some diffs got duplicated. Some showed up in the wrong order. All of these break the diff algorithm, and that&apos;s &lt;strong&gt;the last thing you want from a diff tool.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But here&apos;s a different way to look at it: Could I have sat down and developed an algorithm that solves this well? I don&apos;t think I could have. Before AI, I would have tried anyway. I would probably have written a basic version, maybe just as bad as the AI&apos;s, maybe a little better, who knows. But &lt;strong&gt;it would have taken a long, long time.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As the edge cases appeared, I wrote tests and fixed individual cases. Claude and I never redesigned a solution. Code quality went down. The more this happened, the more other things broke, until &lt;strong&gt;it got really hard to add anything at all.&lt;/strong&gt; This is not specific to agents. &lt;strong&gt;This is just how coding works.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now this code is a legacy system, and I love legacy systems. They hold all the knowledge you need to build something great. You only have to read the code and figure out what algorithm would actually cover all the cases. You have to generalize it. And in the process of covering those edge cases, asking Claude to fix them, and reviewing the code, &lt;strong&gt;I developed a feeling for the algorithm.&lt;/strong&gt; What is that feeling if not experience grounded in years of software development? That&apos;s important to me: even working through Claude, I still get the feeling for the algorithm I am after.&lt;/p&gt;
&lt;p&gt;Now I have tests, &lt;strong&gt;feedback, experience, and real usage data&lt;/strong&gt; to check that the feeling is pointing in the right direction. I can feed all of that into Claude and design the algorithm along with it.&lt;/p&gt;
&lt;p&gt;Is it going to be good enough? Probably good enough for all the cases we already have, and maybe better for future ones, since a generalization should hold up better for what&apos;s coming. Maybe new cases show up that it doesn&apos;t handle well. That is just how software works.&lt;/p&gt;
&lt;p&gt;From my point of view, here&apos;s what happened: I imagined a diff tool that would give me better context and let me review code better. Then I built it with the tools I have. &lt;strong&gt;The tools I have now are better and faster than before.&lt;/strong&gt; That&apos;s it.&lt;/p&gt;
&lt;p&gt;What I love about programming is that &lt;strong&gt;I can take my imagination and make it tangible.&lt;/strong&gt; Make it work. Make it move. Make it a machine that does things on its own. That&apos;s what software is to me, a machine that does things on its own, and &lt;strong&gt;it&apos;s beautiful to watch.&lt;/strong&gt; I could always do that with code. Now I can do it a hundred times faster, and it&apos;s the same feeling.&lt;/p&gt;</content:encoded><category>ai</category><category>blog</category></item><item><title>Diffs for reviewing agentic code</title><link>https://juanibiapina.dev/posts/2026-04-21-scope-expanded-diffs/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-04-21-scope-expanded-diffs/</guid><description>How do I keep track of so much code?</description><pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I review a lot of AI generated code.&lt;/p&gt;
&lt;p&gt;Here&apos;s what it looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;    line: &amp;#x26;str,
    width: usize,
    scopes: &amp;#x26;[HunkScopes],
    scope_expanded: bool, // [!code ++]
) -&gt; Vec&amp;#x3C;Line&amp;#x3C;&apos;static&gt;&gt; {
    if let Some(content) = line.strip_prefix(&apos;+&apos;).filter(|_| !line.starts_with(&quot;+++&quot;)) {
        return vec![syntax_diff_line(content, DIFF_ADDED_BG, &amp;#x26;entry.path, width)];
    // ...
                .find(|s| s.hunk_new_start == new_start)
                .map(|s| s.ancestors.as_slice())
        });
        return render_hunk_separator(line, &amp;#x26;entry.path, width, ancestors); // [!code --]
        return render_hunk_separator(line, &amp;#x26;entry.path, width, ancestors, scope_expanded); // [!code ++]
    }

    vec![Line::from(Span::raw(fit_line(line, width)))]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is even going on?&lt;/p&gt;
&lt;p&gt;Try this version instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn render_diff_line(
    entry: &amp;#x26;HistoryEntry,
    line: &amp;#x26;str,
    width: usize,
    scopes: &amp;#x26;[HunkScopes],
    scope_expanded: bool, // [!code ++]
) -&gt; Vec&amp;#x3C;Line&amp;#x3C;&apos;static&gt;&gt; {
    if let Some(content) = line.strip_prefix(&apos;+&apos;).filter(|_| !line.starts_with(&quot;+++&quot;)) {
        return vec![syntax_diff_line(content, DIFF_ADDED_BG, &amp;#x26;entry.path, width)];
    }
    if let Some(content) = line.strip_prefix(&apos;-&apos;).filter(|_| !line.starts_with(&quot;---&quot;)) {
        return vec![syntax_diff_line(
            content,
            DIFF_DELETED_BG,
            &amp;#x26;entry.path,
            width,
        )];
    }
    if let Some(content) = line.strip_prefix(&apos; &apos;) {
        return vec![syntax_diff_line(content, Color::Reset, &amp;#x26;entry.path, width)];
    }
    if line.starts_with(&quot;@@&quot;) {
        let ancestors = parse_hunk_new_start(line).and_then(|new_start| {
            scopes
                .iter()
                .find(|s| s.hunk_new_start == new_start)
                .map(|s| s.ancestors.as_slice())
        });
        return render_hunk_separator(line, &amp;#x26;entry.path, width, ancestors); // [!code --]
        return render_hunk_separator(line, &amp;#x26;entry.path, width, ancestors, scope_expanded); // [!code ++]
    }

    vec![Line::from(Span::raw(fit_line(line, width)))]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shows the entire function, and it&apos;s easy to see what changed: a new &lt;code&gt;scope_expanded&lt;/code&gt; argument is added to the signature and passed through to &lt;code&gt;render_hunk_separator&lt;/code&gt;. Easy to see in this diff. It also shows the entire function, which allows me to keep learning about the codebase since I don&apos;t literally type the code anymore.&lt;/p&gt;
&lt;p&gt;Here is another example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;    entry_indices: Vec&amp;#x3C;usize&gt;,
    diff_scroll: usize,
    diff_cache: Option&amp;#x3C;DiffCache&gt;,
    show_expanded: bool, // [!code ++]
}

    // ...
            entry_indices: vec![0; trace_count],
            diff_scroll: 0,
            diff_cache: None,
            show_expanded: true, // [!code ++]
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see a field is added and initialized, but it doesn&apos;t even show to what. Here&apos;s what I want to see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;struct AppState {
    focus: Focus,
    trace_index: usize,
    entry_indices: Vec&amp;#x3C;usize&gt;,
    diff_scroll: usize,
    diff_cache: Option&amp;#x3C;DiffCache&gt;,
    show_expanded: bool, // [!code ++]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;    fn new(trace_count: usize) -&gt; Self {
        Self {
            focus: Focus::Entries,
            trace_index: 0,
            entry_indices: vec![0; trace_count],
            diff_scroll: 0,
            diff_cache: None,
            show_expanded: true, // [!code ++]
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is possible using &lt;code&gt;git diff -W&lt;/code&gt;, which can then be easily piped into &lt;a href=&quot;https://github.com/dandavison/delta&quot;&gt;delta&lt;/a&gt;. There&apos;s also &lt;a href=&quot;https://github.com/Wilfred/difftastic&quot;&gt;difftastic&lt;/a&gt; which uses tree-sitter for diffing but it&apos;s limited to controlling diff context by number of lines.&lt;/p&gt;
&lt;p&gt;What if we build something?&lt;/p&gt;</content:encoded><category>ai</category><category>tooling</category><category>tree-sitter</category></item><item><title>Zen in Coding Agents</title><link>https://juanibiapina.dev/posts/2026-04-19-the-zen-of-coding-agents/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-04-19-the-zen-of-coding-agents/</guid><description>LLM output is a very useful set of random words.</description><pubDate>Sun, 19 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;LLM output is a very useful set of random words.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My friend &lt;a href=&quot;https://kaofelix.dev/&quot;&gt;Kao Félix&lt;/a&gt; always says something like that.&lt;/p&gt;
&lt;p&gt;Like all zen ideas, this one is hard to see.&lt;/p&gt;
&lt;p&gt;LLM output looks like real talk. It triggers your deepest emotions and fears. It is incredibly useful.&lt;/p&gt;
&lt;p&gt;But LLM output follows no philosophy, mental model, cultural norms, ethics, reason or even logic. &lt;strong&gt;It is a process&lt;/strong&gt;. And it&apos;s a process that we don&apos;t fully understand. We don&apos;t even know if the process is, by its nature, understandable.&lt;/p&gt;
&lt;p&gt;Agentic output contains all &lt;strong&gt;combined knowledge of humankind&lt;/strong&gt;, but it&apos;s also nothing.&lt;/p&gt;
&lt;p&gt;It doesn&apos;t get any more Zen than that.&lt;/p&gt;</content:encoded><category>ai</category><category>zen</category></item><item><title>The paper-first rule</title><link>https://juanibiapina.dev/posts/2026-04-11-paper-first/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-04-11-paper-first/</guid><description>Using paper to bring back focus and intentionality to AI coding</description><pubDate>Sat, 11 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;With AI coding agents, it is very easy to jump straight into coding.&lt;/p&gt;
&lt;p&gt;I can build a prototype, test a UI, throw together a mockup, start shaping an idea in minutes. It is also very fun to do that.&lt;/p&gt;
&lt;p&gt;On top of that, I designed my dev environment to remove friction. I can jump into a project and start coding instantly. Tmux sessions persist. Neovim is always ready. Custom git scripts automate flows. I used to run multiple projects in parallel. I could use git worktrees before AI coding agents existed. I put a lot of effort into removing any friction for coding. It&apos;s very personal, but you can find about 10 years of optimizations in my &lt;a href=&quot;https://github.com/juanibiapina/dotfiles&quot;&gt;dotfiles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This does not work anymore.&lt;/p&gt;
&lt;p&gt;Nothing changed with AI, but everything changed.&lt;/p&gt;
&lt;p&gt;Now it is too easy to start a new project in the middle of another one, because &lt;strong&gt;I do not have to literally type the code&lt;/strong&gt;. I can branch into a new idea with almost no cost. That means I start too many things and think less about each of them.&lt;/p&gt;
&lt;p&gt;Is this the part where we complain that AI is ruining our attention spans?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;This is the part where I fix it.&lt;/p&gt;
&lt;p&gt;Enter &lt;strong&gt;paper first&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before producing any code or starting any new project, I must write about it by hand on paper.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not in a note-taking app. Not in a scratch buffer. Not in a prompt.&lt;/p&gt;
&lt;p&gt;On paper. With my hand like it&apos;s 1999 and &lt;a href=&quot;https://www.imdb.com/title/tt0133093/&quot;&gt;The Matrix&lt;/a&gt; just came out. This could also be &lt;a href=&quot;https://simone.org/deliberate-friction/&quot;&gt;deliberate friction&lt;/a&gt; and &lt;a href=&quot;https://mariozechner.at/posts/2026-03-25-thoughts-on-slowing-the-fuck-down&quot;&gt;slowing the fuck down&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This changed my mindset from one day to the next. I focus more. I feel less anxious. I write more. I sleep better. And I still do not have to literally type the code when the time comes.&lt;/p&gt;
&lt;p&gt;We&apos;ll see what happens over the next few weeks. I expect I&apos;ll get generally better results. &lt;a href=&quot;https://matterco.co/the-maybe-story/&quot;&gt;Maybe.&lt;/a&gt; Or, if you prefer, &lt;a href=&quot;https://www.youtube.com/watch?v=sWd6fNVZ20o&quot;&gt;Alan Watts can tell it better&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Go paper first.&lt;/p&gt;</content:encoded><category>ai</category><category>writing</category><category>focus</category><category>craft</category></item><item><title>Anthropic banned me for trying to use Claude Max on pi</title><link>https://juanibiapina.dev/posts/2026-04-08-banned-by-anthropic/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-04-08-banned-by-anthropic/</guid><description>Reminder of what I care about</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today I got banned by Anthropic.&lt;/p&gt;
&lt;p&gt;I was trying to use my Claude Max subscription on &lt;a href=&quot;https://pi.dev&quot;&gt;pi&lt;/a&gt;. Sorry :(&lt;/p&gt;
&lt;p&gt;I felt so sad.&lt;/p&gt;
&lt;p&gt;I wanted was to use pi with Opus 4.6. Yes, I could use the API instead, but I can&apos;t afford the cost for the amount of use I make.&lt;/p&gt;
&lt;p&gt;Why didn&apos;t I just use Claude Code? It probably would have worked well. In fact, it probably would have worked better than pi, because I would not have spent countless hours customizing every minimal aspect of my setup.&lt;/p&gt;
&lt;p&gt;But then, would it be worth it?&lt;/p&gt;
&lt;p&gt;I am a computer scientist. I use and craft software that lives in the soul of the computer. I am in this for the craft. I love the journey: building, designing, learning, and interacting with machines. And now I can talk to it and IT TALKS BACK. That still feels like a dream.&lt;/p&gt;
&lt;p&gt;Unfortunately I don&apos;t want to work with closed source software.&lt;/p&gt;
&lt;p&gt;I customize my software.&lt;/p&gt;
&lt;p&gt;My software is my craft.&lt;/p&gt;
&lt;p&gt;My software is my life.&lt;/p&gt;</content:encoded><category>ai</category><category>anthropic</category><category>claude</category><category>pi</category><category>software</category><category>craft</category><category>learning</category></item><item><title>How can I share the code to my autonomous agent?</title><link>https://juanibiapina.dev/posts/2026-03-27-how-can-i-share-the-code-to-my-autonomous-agent/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2026-03-27-how-can-i-share-the-code-to-my-autonomous-agent/</guid><description>It&apos;s all just prompts.</description><pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;How can I share the code for my autonomous agent if I cannot recommend people to use it? In fact, I say do not use &lt;a href=&quot;https://github.com/openclaw/openclaw&quot;&gt;OpenClaw&lt;/a&gt;, do not use &lt;a href=&quot;https://zeroclaw.net/&quot;&gt;ZeroClaw&lt;/a&gt;, do not use any agent framework. And to be clear, I&apos;m talking to my audience, which is often software developers with access to coding agents.&lt;/p&gt;
&lt;p&gt;The reason is that almost every product that has come out in the last few months is &lt;a href=&quot;https://medium.com/@alexanderhuseby/most-ai-startups-are-just-prompt-wrappers-5ce52eaf26ef&quot;&gt;just prompting&lt;/a&gt;. In fact, most products for the last few years have just been &lt;a href=&quot;https://dev.to/dev_tips/the-graveyard-of-ai-startups-startups-that-forgot-to-build-real-value-5ad9&quot;&gt;different ways to prompt&lt;/a&gt;. A few haven&apos;t, but most of them have. So for example, if you hear about OpenClaw and you learn that it memorizes things about you and it learns as you go, all you need to do is &lt;a href=&quot;https://medium.com/@NMitchem/ai-agents-dont-need-your-developer-tools-7f6adebb479c&quot;&gt;prompt your agent with that&lt;/a&gt;. You tell it, you should from now on memorize facts about me and learn as you go. There you go, you&apos;ve &lt;a href=&quot;https://dev.to/taskconcierge/stop-building-ai-agents-build-ai-tools-instead-51aj&quot;&gt;replicated the entire framework&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a problem for me because on one hand, I&apos;m a publisher of software. I want to publish software. Worst case, I want to publish prompts, but &lt;a href=&quot;https://dennisstanoev.com/building-an-ai-agent-from-scratch-without-frameworks/&quot;&gt;there&apos;s nothing to publish&lt;/a&gt;. For my audience specifically, I don&apos;t want to say, hey, here&apos;s my agent framework. It&apos;s one more framework among a hundred.&lt;/p&gt;
&lt;p&gt;The code for my agent has hard-coded stuff. It&apos;s very specific to how I do things. Its memory is not just a general store that it reads and writes to. It&apos;s actually &lt;a href=&quot;https://medium.com/@danbork/my-ais-memory-lives-in-obsidian-and-i-can-see-every-bit-of-it-cdcad1113887&quot;&gt;connected to my notes&lt;/a&gt; in a way that it &lt;a href=&quot;https://daveswift.com/openclaw-obsidian-memory/&quot;&gt;understands how I think&lt;/a&gt;. I continue to edit these notes manually, which is part of the workflow.&lt;/p&gt;
&lt;p&gt;A big part of this is that &lt;a href=&quot;https://medium.com/@eren.m.nevin/personalized-software-when-ai-turns-users-into-co-developers-d76cc60d6a04&quot;&gt;software is now customizable per person&lt;/a&gt;. The reason there are so many different agent frameworks is that everybody can build their own and they&apos;re slightly different. It&apos;s &lt;a href=&quot;https://www.reddit.com/r/AI_Agents/comments/1msm2mw/id_rather_build_my_own_ai_tools_than_pay_for/&quot;&gt;easier to build your own&lt;/a&gt; than to customize someone else&apos;s. It&apos;s easier to build your own from scratch than to take a &lt;a href=&quot;https://rmeerasahib.substack.com/p/personalized-software-is-coming-the&quot;&gt;generic framework&lt;/a&gt; and use it.&lt;/p&gt;
&lt;p&gt;That said, the &lt;a href=&quot;https://pi.dev&quot;&gt;pi coding agent&lt;/a&gt; is amazing. Pi is the vim of coding agents.&lt;/p&gt;</content:encoded><category>ai</category><category>agents</category></item><item><title>Replicating the full macOS ABC Extended Keyboard with XKB</title><link>https://juanibiapina.dev/posts/2025-06-05-replicating-the-full-macos-abc-extended-keyboard-with-xkb/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2025-06-05-replicating-the-full-macos-abc-extended-keyboard-with-xkb/</guid><description>With the help of AI, of course.</description><pubDate>Thu, 05 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the &lt;a href=&quot;/posts/2025-06-04-mac-abc-extended-on-nixos&quot;&gt;previous post&lt;/a&gt; I created a fix for some of the missing dead keys in the &lt;code&gt;us(mac)&lt;/code&gt; layout I use on NixOS&lt;/p&gt;
&lt;p&gt;Today I thought, why not simply replicate the entire layout?&lt;/p&gt;
&lt;p&gt;A quick search (with ChatGPT) gave me this &lt;a href=&quot;https://github.com/RedBearAK/optspecialchars/blob/main/Option-key-characters_ABC-Extended.md&quot;&gt;great resource&lt;/a&gt; with an entire spec for the ABC Extended layout.&lt;/p&gt;
&lt;p&gt;It was only a matter of giving ChatGPT an example and telling it to convert everything to xkb.
Here&apos;s the result, althought I haven&apos;t actually tested every single case:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;default partial default alphanumeric_keys
xkb_symbols &quot;extended&quot; {
  name[Group1]= &quot;ABC Extended (macOS)&quot;;

  key.type[group1]=&quot;FOUR_LEVEL&quot;;

  // Numbers row
  key &amp;#x3C;TLDE&gt; { [ grave, asciitilde, dead_grave, dead_horn ] };
  key &amp;#x3C;AE01&gt; { [ 1, exclam, exclamdown, U2044 ] };
  key &amp;#x3C;AE02&gt; { [ 2, at, trademark, EuroSign ] };
  key &amp;#x3C;AE03&gt; { [ 3, numbersign, sterling, U2039 ] };
  key &amp;#x3C;AE04&gt; { [ 4, dollar, cent, U203A ] };
  key &amp;#x3C;AE05&gt; { [ 5, percent, section, U2020 ] };
  key &amp;#x3C;AE06&gt; { [ 6, asciicircum, dead_circumflex, U0302 ] };
  key &amp;#x3C;AE07&gt; { [ 7, ampersand, paragraph, U2021 ] };
  key &amp;#x3C;AE08&gt; { [ 8, asterisk, bullet, degree ] };
  key &amp;#x3C;AE09&gt; { [ 9, parenleft, ordfeminine, periodcentered ] };
  key &amp;#x3C;AE10&gt; { [ 0, parenright, masculine, U201A ] };
  key &amp;#x3C;AE11&gt; { [ minus, underscore, endash, emdash ] };
  key &amp;#x3C;AE12&gt; { [ equal, plus, notequal, plusminus ] };

  // QWERTY row
  key &amp;#x3C;AD01&gt; { [ q, Q, oe, OE ] };
  key &amp;#x3C;AD02&gt; { [ w, W, dead_abovedot, U0307 ] };
  key &amp;#x3C;AD03&gt; { [ e, E, dead_acute, U0301 ] };
  key &amp;#x3C;AD04&gt; { [ r, R, registered, permille ] };
  key &amp;#x3C;AD05&gt; { [ t, T, thorn, THORN ] };
  key &amp;#x3C;AD06&gt; { [ y, Y, yen, U030F ] };
  key &amp;#x3C;AD07&gt; { [ u, U, dead_diaeresis, U0308 ] };
  key &amp;#x3C;AD08&gt; { [ i, I, dead_horn, U031B ] };
  key &amp;#x3C;AD09&gt; { [ o, O, Oslash, Oslash ] };
  key &amp;#x3C;AD10&gt; { [ p, P, dead_belowcomma, U0326 ] };
  key &amp;#x3C;AD11&gt; { [ bracketleft, braceleft, leftdoublequotemark, rightdoublequotemark ] };
  key &amp;#x3C;AD12&gt; { [ bracketright, braceright, leftsinglequotemark, rightsinglequotemark ] };
  key &amp;#x3C;BKSL&gt;  { [ backslash, bar, guillemotleft, guillemotright ] };

  // ASDF row
  key &amp;#x3C;AC01&gt; { [ a, A, dead_macron, U0304 ] };
  key &amp;#x3C;AC02&gt; { [ s, S, ssharp, U0311 ] };
  key &amp;#x3C;AC03&gt; { [ d, D, eth, ETH ] };
  key &amp;#x3C;AC04&gt; { [ f, F, function, U0330 ] };
  key &amp;#x3C;AC05&gt; { [ g, G, copyright, U2038 ] };
  key &amp;#x3C;AC06&gt; { [ h, H, dead_macronbelow, U0331 ] };
  key &amp;#x3C;AC07&gt; { [ j, J, dead_doubleacute, U030B ] };
  key &amp;#x3C;AC08&gt; { [ k, K, dead_abovering, U030A ] };
  key &amp;#x3C;AC09&gt; { [ l, L, dead_stroke, U0335 ] };
  key &amp;#x3C;AC10&gt; { [ semicolon, colon, ellipsis, U2116 ] };
  key &amp;#x3C;AC11&gt; { [ apostrophe, quotedbl, ae, AE ] };

  // ZXCV row
  key &amp;#x3C;AB01&gt; { [ z, Z, dead_hook, U0309 ] };
  key &amp;#x3C;AB02&gt; { [ x, X, dead_belowdot, U0323 ] };
  key &amp;#x3C;AB03&gt; { [ c, C, dead_cedilla, U0327 ] };
  key &amp;#x3C;AB04&gt; { [ v, V, dead_caron, U030C ] };
  key &amp;#x3C;AB05&gt; { [ b, B, dead_breve, U0306 ] };
  key &amp;#x3C;AB06&gt; { [ n, N, dead_tilde, U0303 ] };
  key &amp;#x3C;AB07&gt; { [ m, M, dead_ogonek, U0328 ] };
  key &amp;#x3C;AB08&gt; { [ comma, less, lessthanequal, U201E ] };
  key &amp;#x3C;AB09&gt; { [ period, greater, greaterthanequal, U0294 ] };
  key &amp;#x3C;AB10&gt; { [ slash, question, division, questiondown ] };

  include &quot;level3(ralt_switch)&quot;
};
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>keyboard</category><category>nixos</category><category>linux</category><category>macos</category><category>xkb</category><category>unicode</category></item><item><title>Mac ABC Extended layout on NixOS</title><link>https://juanibiapina.dev/posts/2025-06-04-mac-abc-extended-on-nixos/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2025-06-04-mac-abc-extended-on-nixos/</guid><description>So I can type many languages without switching layouts in both NixOS and macOS</description><pubDate>Wed, 04 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I previously discovered &lt;a href=&quot;/posts/2025-05-25-i-can-now-type-z%CC%A7/&quot;&gt;I can now type &lt;code&gt;ţ&lt;/code&gt; on macOS&lt;/a&gt;, but
now I want to have my Linux layout match macOS ABC Extended so I can type with the same dead keys on both systems.&lt;/p&gt;
&lt;p&gt;I already use the &lt;code&gt;xkb_variant mac&lt;/code&gt;. It matches some of the dead keys like &lt;code&gt;e&lt;/code&gt;, &lt;code&gt;i&lt;/code&gt;, &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;u&lt;/code&gt; which are great for Portuguese (é, â) and German (ü, ß):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;input &quot;type:keyboard&quot; {
  xkb_model pc104
  xkb_layout us
  xkb_variant mac
  # lv3:lalt_switch makes the left Alt key a third-level chooser key (keyd remaps Start to be Alt)
  xkb_options &quot;lv3:lalt_switch&quot;
  repeat_delay 250
  repeat_rate 40
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But as it turns out, &lt;code&gt;option+c&lt;/code&gt; in this variant outputs &lt;code&gt;ç&lt;/code&gt;, not a dead &lt;code&gt;,&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s fix it because who can live without &lt;code&gt;ţ&lt;/code&gt;? ţac-pac!!&lt;/p&gt;
&lt;p&gt;First we need a new layout that inherits from my current one &lt;code&gt;us(mac)&lt;/code&gt; and simply overrides the one key we need, &lt;code&gt;c&lt;/code&gt; (&lt;code&gt;AB03&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;default partial default alphanumeric_keys
xkb_symbols &quot;extended-sequoia&quot; {
  include &quot;us(mac)&quot;

  name[Group1]= &quot;layout matching ABC extended on macos Sequoia&quot;;

  key &amp;#x3C;AB03&gt; { [ c, C,  dead_cedilla, dead_cedilla ] };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we install it in NixOS with a configuration option:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;services.xserver = {
  enable = true;

  xkb = {
    model = &quot;pc104&quot;;
    layout = &quot;abc&quot;;
    variant = &quot;extended-sequoia&quot;;
    options = &quot;terminate:ctrl_alt_bksp,lv3:lwin_switch&quot;;
    extraLayouts = {
      &quot;abc&quot; = {
        description = &quot;ABC layout similar to macOS&quot;;
        languages = [ &quot;eng&quot; ];
        symbolsFile = ./symbols/abc.xkb;
      };
    };
  };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in Sway config:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;input &quot;type:keyboard&quot; {
  xkb_model pc104
  xkb_layout abc
  xkb_variant extended-sequoia
  # lv3:lalt_switch makes the left Alt key a third-level chooser key (keyd remaps Start to be Alt)
  xkb_options &quot;lv3:lalt_switch&quot;
  repeat_delay 250
  repeat_rate 40
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There we go! Şåa-ţi c∫a m∫a f∫acåur∫am! &lt;strong&gt;OH NO&lt;/strong&gt;. Some more fixes for &lt;code&gt;macron&lt;/code&gt; and &lt;code&gt;breve&lt;/code&gt; in Romanian:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;default partial default alphanumeric_keys
xkb_symbols &quot;extended-sequoia&quot; {
  include &quot;us(mac)&quot;

  name[Group1]= &quot;layout matching ABC extended on macos Sequoia&quot;;

  key &amp;#x3C;AC01&gt; { [ a, A,  dead_macron , dead_macron  ] };

  key &amp;#x3C;AB03&gt; { [ c, C,  dead_cedilla, dead_cedilla ] };
  key &amp;#x3C;AB05&gt; { [ b, B,  dead_breve  , dead_breve   ] };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now I have a place where I can fix any differences between keys on macOS and NixOS.&lt;/p&gt;
&lt;p&gt;The final (and eventually probably updated) version can be found in &lt;a href=&quot;https://github.com/juanibiapina/dotfiles/blob/800babce29116fae7d80b6f4186c769c0a435c91/nix/hosts/desktop/symbols/abc.xkb&quot;&gt;my dotfiles&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;How to define new xkb layouts: https://www.x.org/releases/current/doc/xorg-docs/input/XKB-Enhancing.html#Defining_New_Layouts&lt;/li&gt;
&lt;li&gt;Source code for &lt;code&gt;us(mac)&lt;/code&gt; xkb_variant: https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/master/symbols/us#L730&lt;/li&gt;
&lt;li&gt;Custom XKB layouts in NixOS Manual: https://nixos.org/manual/nixos/stable/#custom-xkb-layouts&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>keyboard</category><category>nixos</category><category>linux</category><category>macos</category><category>xkb</category><category>unicode</category></item><item><title>I can now type z̧</title><link>https://juanibiapina.dev/posts/2025-05-25-i-can-now-type-z%CC%A7/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2025-05-25-i-can-now-type-z%CC%A7/</guid><description>Why would I want to type that?</description><pubDate>Sun, 25 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I upgraded my MacBook (named &lt;code&gt;macm1&lt;/code&gt;) to MacOS Whatever and it seems like the way you input &lt;a href=&quot;https://www.wordnik.com/words/cedilla&quot;&gt;cedilla&lt;/a&gt; in the ABC Extended keyboard has changed.&lt;/p&gt;
&lt;p&gt;I think that pressing &lt;code&gt;option+c&lt;/code&gt; used to output &lt;code&gt;ç&lt;/code&gt; but now it&apos;s a dead key for &lt;code&gt;¸&lt;/code&gt;.
A dead key means that the &lt;code&gt;¸&lt;/code&gt; can be combined with other keys to generate combinations like: &lt;code&gt;ņ&lt;/code&gt;, &lt;code&gt;ẹ&lt;/code&gt; and of course &lt;code&gt;ç&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You may ask: when would I ever need to type a combination like &lt;code&gt;¸&lt;/code&gt; or &lt;code&gt;ḩ&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Well, here it is, a blog post that needed it.&lt;/p&gt;
&lt;p&gt;Also, my girlfriend is from Galați.&lt;/p&gt;</content:encoded><category>keyboard</category><category>macos</category><category>unicode</category></item><item><title>Blood on the Clocktower, software development and making life decisions</title><link>https://juanibiapina.dev/posts/2025-04-28-blood-on-the-clocktower/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2025-04-28-blood-on-the-clocktower/</guid><description>What&apos;s a framework for decisions making in life?</description><pubDate>Mon, 28 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I played a game the other day called &lt;a href=&quot;https://bloodontheclocktower.com/&quot;&gt;Blood on the Clocktower&lt;/a&gt;.
It’s a social deduction game where a team of good townsfolk needs to kill a demon to win.
It’s similar to other social deduction games like &lt;a href=&quot;https://www.secrethitler.com/&quot;&gt;Secret Hitler&lt;/a&gt; or &lt;a href=&quot;https://boardgamegeek.com/boardgame/41114/the-resistance&quot;&gt;The Resistance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One big difference is that Blood on the Clocktower has a storyteller who controls some aspects of the game.
For example, the storyteller chooses what information each player receives, based on their character’s abilities and effects.&lt;/p&gt;
&lt;p&gt;At one point during the game, my friend Sam — the storyteller — made a mistake.
According to the rules, a certain ability should have &lt;a href=&quot;https://www.reddit.com/r/gaming/comments/11evu3c/what_the_hell_does_proc_mean/&quot;&gt;proced&lt;/a&gt;, giving us some potentially false information (because the character was &lt;a href=&quot;https://wiki.bloodontheclocktower.com/Poisoner&quot;&gt;poisoned&lt;/a&gt;.
But instead, the ability didn&apos;t proc at all.
The fact that nothing happened was a clear sign that the character was poisoned — something that would have been much less obvious if the ability had proced as normal.&lt;/p&gt;
&lt;p&gt;When I asked about it later, Sam said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You have to make a choice and stick to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That mindset really stuck with me.&lt;/p&gt;
&lt;p&gt;What I found striking was that Sam didn&apos;t have to double down on that decision.
He could have admitted to a one-time mistake, restarted the game, or tried to patch it somehow.
Instead, he accepted what had happened and committed to it.&lt;/p&gt;
&lt;p&gt;Part of why that worked is the scope of the situation: at worst, we were only affecting one timeboxed game.
But more importantly, Sam avoided the biggest danger — the mental load of second-guessing your own decisions.
That load can be overwhelming.
I&apos;ve seen it happen to others, and I&apos;ve felt it myself.&lt;/p&gt;
&lt;p&gt;In software development, we &lt;a href=&quot;https://www.agile-academy.com/en/agile-dictionary/fail-fast/&quot;&gt;fail fast&lt;/a&gt;, iterate quickly, timebox work and &lt;a href=&quot;https://caroli.org/en/livro/fun-retrospectives-activities-and-ideas-for-making-agile-retrospectives-more-engaging/&quot;&gt;retrospect&lt;/a&gt;.
We accept mistakes as part of the process and move forward.&lt;/p&gt;
&lt;p&gt;What about life?
What practices do we have to keep ourselves from being paralyzed by doubt and mistakes?&lt;/p&gt;</content:encoded><category>software-design</category></item><item><title>Installing shell scripts from Github with Basher</title><link>https://juanibiapina.dev/posts/2014-11-05-installing-scripts-from-github-with-basher/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2014-11-05-installing-scripts-from-github-with-basher/</guid><description>How to install bash packages by running a one liner.</description><pubDate>Wed, 05 Nov 2014 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;The problem&lt;/h3&gt;
&lt;p&gt;On my &lt;a href=&quot;/posts/2014-08-20-basher-a-package-manager-for-shell-scripts/&quot;&gt;previous post about Basher&lt;/a&gt; I mentioned that one of its goals was to be able to install scripts directly from github with minimal manual intervention. During the last month, I had to make some changes to get closer to that goal.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;package.sh&lt;/code&gt; file contains package information like binaries, completions, dependencies etc. The problem with having a package descriptor is that I need to fork every repo I want to install and add the file (maybe pull request a change for the maintainer to add the file). There is nothing wrong with this apprach, and it is in fact what &lt;a href=&quot;https://github.com/bpkg/bpkg&quot;&gt;bpkg&lt;/a&gt; does. This is partly what allows them to have custom install scripts per package.&lt;/p&gt;
&lt;p&gt;On the other hand, what I really wanted was to see a package on github, run a one liner and keep working.&lt;/p&gt;
&lt;p&gt;Besides, sourcing the package.sh file was a silly security problem. In addition, I never felt really confortable with having two different formats for packages (basher and bpkg). The discussion evolved but not fast enough for me.&lt;/p&gt;
&lt;p&gt;So I got rid of it.&lt;/p&gt;
&lt;h3&gt;Conventions to the rescue&lt;/h3&gt;
&lt;p&gt;Now, basher looks for a &lt;code&gt;bin&lt;/code&gt; folder and links any files in there. If there is no &lt;code&gt;bin&lt;/code&gt; folder, it links all executable files in the package root.&lt;/p&gt;
&lt;p&gt;With this change, I&apos;ve been very happy installing packages with one liners.&lt;/p&gt;
&lt;h3&gt;Working packages&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;sstephenson/bats&lt;/strong&gt; Bash Automated Testing System&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pote/gpm&lt;/strong&gt; Go package manager&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pote/gvp&lt;/strong&gt; Go versioning packager&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;treyhunner/tmuxstart&lt;/strong&gt; named tmux sessions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;zsh-users/antigen&lt;/strong&gt; plugin manager for zsh&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bripkens/dock&lt;/strong&gt; easy bootstrapper using docker&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Kind of working packages&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;holman/spark&lt;/strong&gt; it also links an extra &lt;code&gt;test&lt;/code&gt; executable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hecticjeff/shoreman&lt;/strong&gt; binary ends with &apos;.sh&apos;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Future work&lt;/h3&gt;
&lt;p&gt;I want to automatically remove the &apos;.sh&apos; from binaries. Also figure out how to install completions for packages.&lt;/p&gt;</content:encoded><category>basher</category><category>shell</category><category>open-source</category></item><item><title>A Package Manager For Shell Scripts</title><link>https://juanibiapina.dev/posts/2014-08-20-basher-a-package-manager-for-shell-scripts/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2014-08-20-basher-a-package-manager-for-shell-scripts/</guid><description>Introducing basher.</description><pubDate>Wed, 20 Aug 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post I&apos;ll introduce &lt;a href=&quot;https://github.com/basherpm/basher&quot;&gt;basher&lt;/a&gt;, a package manager for shell scripts.&lt;/p&gt;
&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;I do some development using bash. Not a &lt;a href=&quot;https://github.com/avleen/bashttpd&quot;&gt;web server&lt;/a&gt;, or a &lt;a href=&quot;https://github.com/dominictarr/JSON.sh&quot;&gt;json parser&lt;/a&gt;, because that&apos;s not the point of bash (although fun!), but there are many tasks that are actually faster to do in bash.&lt;/p&gt;
&lt;p&gt;When you write a bash script, it is usually a single file. All you have to do is put it somewhere in your PATH and you&apos;re done. You can just keep using it and forget about it. Unless you have multiple machines.&lt;/p&gt;
&lt;p&gt;For me, the main problem with this approach is the lack of version control. I tend to get lost easily during development if I don&apos;t have the checkpoints a VCS provides. I need to be able to go back and forth in time and experiment. With the script in a version controlled directory, I only need to link the bin somewhere in the PATH. I can also publish to github.&lt;/p&gt;
&lt;p&gt;It is very difficult to write bash scripts and get it right the first time. You might forget a space before a &lt;code&gt;]&lt;/code&gt;, or you might put an extra space after an &lt;code&gt;=&lt;/code&gt;. Or you might forget some quotes. The solution I found to this problem is TDD, of course. There is a testing framework for bash called &lt;a href=&quot;https://github.com/sstephenson/bats&quot;&gt;bats&lt;/a&gt;, and it makes testing bash scripts as easy as it gets.&lt;/p&gt;
&lt;p&gt;On the other hand, now you have to install a third party bash script (bats itself is written in bash). It&apos;s not just a single file you can copy, but the creators of bats are cool enough to provide a simple, reliable and well made install.sh script that copies the binaries to the right place. Still, you have to clone the project and find out how to install it. Remember you might need sudo.&lt;/p&gt;
&lt;p&gt;Bats is also on brew, so that&apos;s simpler if you&apos;re on osx. There is probably something packaged for linux too, but which distros? Older install instructions for bats told you to clone the repo to &lt;code&gt;~/.bats&lt;/code&gt; and add its &lt;code&gt;bin&lt;/code&gt; directory to your path. That&apos;s how I had to do for a while. In the end, that was the most reliable approach that I could reproduce among all my machines.&lt;/p&gt;
&lt;p&gt;I want to publish a couple of bash scripts myself, but I don&apos;t really want to maintain installation instructions for osx and a bunch of linux distros. I want to think of a bash script as a package for bash, just like ruby has gems.&lt;/p&gt;
&lt;p&gt;I also want to go to github, find a script I like, install it with one line and use it. If one of my machines doesn&apos;t have that script, all I need to do is run that one line and get it. No messing with my PATH either.&lt;/p&gt;
&lt;h2&gt;Basher&lt;/h2&gt;
&lt;p&gt;So I wrote &lt;a href=&quot;https://github.com/basherpm/basher&quot;&gt;basher&lt;/a&gt;. With it installed, you can do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ basher install basherpm/bats
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There, &lt;code&gt;bats&lt;/code&gt; is in your PATH ready to be used. Whatever OS.&lt;/p&gt;
&lt;p&gt;The install command looks for a repository on github, clones it to a known location and links the binaries to a place in your PATH.&lt;/p&gt;
&lt;p&gt;I can also list installed packages easily:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ basher list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And uninstall (with completion support):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ basher uninstall basherpm/bats
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or check for outdated packages:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;$ basher outdated
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the &lt;code&gt;commands&lt;/code&gt; command for a full list. Try also &lt;code&gt;basher help &amp;#x3C;command&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Shell support&lt;/h2&gt;
&lt;p&gt;Basher needs to be available in your PATH, of course, but it also needs to add one entry to the PATH, where all binaries will be linked. It does this by hooking into the shell, similarly to what &lt;a href=&quot;https://github.com/sstephenson/rbenv&quot;&gt;rbenv&lt;/a&gt; does. The biggest difference is that &lt;a href=&quot;https://github.com/sstephenson/rbenv&quot;&gt;rbenv&lt;/a&gt; modifies the PATH on the fly, according to what ruby version should be used. Basher is simpler, because it needs only one location added to the PATH.&lt;/p&gt;
&lt;p&gt;If you try running &lt;code&gt;basher init -&lt;/code&gt;, you&apos;ll see what gets added to the shell. This code is generated on the fly, according to what shell you use. It supports bash, zsh (might support any POSIX compliant) and even fish (thanks to &lt;a href=&quot;https://github.com/jvortmann&quot;&gt;João Vortmann&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Packages&lt;/h2&gt;
&lt;p&gt;Basher packages are simply github repos that have a &lt;code&gt;package.sh&lt;/code&gt; file. This file defines what binaries need to be linked when installing and any package dependencies.&lt;/p&gt;
&lt;p&gt;There is also experimental support for package runtimes. It allows you to &lt;code&gt;require juanibiapina/gg&lt;/code&gt; (given this package is installed) in order to make some functions available to your shell. This might change in the future, but I find it very promissing.&lt;/p&gt;
&lt;h2&gt;Why not bpkg ?&lt;/h2&gt;
&lt;p&gt;I was using basher for a while, but did not plan on releasing it, when &lt;a href=&quot;http://www.bpkg.io/&quot;&gt;bpkg&lt;/a&gt; came out. At that point basher was very simple, and bpkg seemed to offer much more. I tried using it, but decided to go back and release basher instead.&lt;/p&gt;
&lt;p&gt;Bpkg uses a &lt;code&gt;package.json&lt;/code&gt; file to define packages. I assumed it was the same format as npm packages, so I got really confused when I checked the documentation for &lt;code&gt;package.json&lt;/code&gt; and found out bpkg implements it incorrectly. It turns out, it is a different format, just with the same name. There is some unfinished discussion going on on &lt;a href=&quot;https://github.com/bpkg/bpkg/issues/17&quot;&gt;this issue&lt;/a&gt;. Overall, I find the choice of name and format confusing.&lt;/p&gt;
&lt;p&gt;Bpkg also doesn&apos;t keep the package repos on your local machine. Instead, it clones the repo, installs the binaries then removes the repo. I wanted to have local copies of each repo.&lt;/p&gt;
&lt;p&gt;Bpkg uses whatever install script is provided by the package. This allows a package to install itself however it wants, including man pages or whatever is needed. On the other hand, it makes it difficult for me to track what has been installed and where. I don&apos;t like installs without the corresponding uninstall, so basher does not provide support for custom install scripts. It would be interesting to add mechanisms for installing man pages, completions etc. That way, any package with a properly defined package.sh would be automatically installable and uninstallable.&lt;/p&gt;
&lt;p&gt;Instead, basher keeps everything under &lt;code&gt;~/.basher&lt;/code&gt;. You can remove this directory and get rid of everything easily.&lt;/p&gt;
&lt;p&gt;Bpkg has support for github releases. You can choose to install a specific version. It also has support for local or global installs, like npm. Basher lacks these features, but they can be added if the need comes. Pull requests are welcome.&lt;/p&gt;
&lt;p&gt;Basher is &lt;a href=&quot;https://travis-ci.org/basherpm/basher&quot;&gt;thoroughly tested&lt;/a&gt;. I failed to make a pull request to bpkg because I lacked confidence.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This has solved many of my shell scripting problems. I can have version controlled scripts automatically linked to the PATH without any changes, I can install scripts from github. It works the same way on my osx, ubuntu and debian (might work anywhere, just need to test). I have one central location where I install scripts. I can easily check for new versions of scripts without checking one by one. I can see the changelog for each script (since I have the full repo cloned locally).&lt;/p&gt;
&lt;p&gt;It also helps me develop scripts. Check &lt;code&gt;basher new&lt;/code&gt; or &lt;code&gt;basher new-command&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is still much that can be done. Pull requests and feedback are very welcome.&lt;/p&gt;</content:encoded><category>basher</category><category>shell</category><category>open-source</category></item><item><title>Shadowing</title><link>https://juanibiapina.dev/posts/2013-11-12-shadowing/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2013-11-12-shadowing/</guid><description>Shadowing in Ruby and obscure language features</description><pubDate>Tue, 12 Nov 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In Ruby you can do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;puts = 1
puts(puts)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What will it output? Let&apos;s follow simple evaluation rules:&lt;/p&gt;
&lt;p&gt;Line 1 assigns &lt;code&gt;1&lt;/code&gt; to the variable &lt;code&gt;puts&lt;/code&gt;. Since &lt;code&gt;puts&lt;/code&gt; is already defined, is is now shadowed in this scope.&lt;/p&gt;
&lt;p&gt;Line 2 reads &lt;code&gt;puts&lt;/code&gt; from the environment yielding &lt;code&gt;1&lt;/code&gt;, and tries to call it as a method. You would expect ruby to complain that 1 is not a method.&lt;/p&gt;
&lt;p&gt;But what really happens?&lt;/p&gt;
&lt;p&gt;It runs fine and it works. &lt;code&gt;puts&lt;/code&gt; used as a variable will look up the variable and find &lt;code&gt;1&lt;/code&gt;. When it is used as a method it will look up the method and ignore the variable.&lt;/p&gt;
&lt;p&gt;There are probably reasons for this, a logical reasoning that is a consequence of the evaluation rules for Ruby. Overall, I don&apos;t care.&lt;/p&gt;
&lt;p&gt;When reading the line &lt;code&gt;puts(puts)&lt;/code&gt; I have to do syntactic analysis to find the word &lt;code&gt;puts&lt;/code&gt; two times. Then I have to do semantic analysis to find out that the first &lt;code&gt;puts&lt;/code&gt; is a method and the second is a variable. Then I have to follow the lookup rules for these two different concepts up the chain of environments. It&apos;s even funnier if you write it like this: &lt;code&gt;puts puts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I am not a compiler. I&apos;ll say again what is one of the worst things you can force the readers of your code to do: Mental Mapping. I don&apos;t want to compile code and jump through several hoops in my head while trying to understand business logic.&lt;/p&gt;
&lt;p&gt;Of course you should never be doing anything even close to this. You should be avoiding shadowing in general (except for very simple cases), you should not be exploiting any potentially obscure features of the language. Trying to go as far away from complications as you can.&lt;/p&gt;
&lt;p&gt;And this is a silly example, right? You would never find code like this in production.&lt;/p&gt;
&lt;p&gt;Sure.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;def clear_some_cache(id, clear_other_cache = false)
  do_something
  clear_other_cache(id) if clear_other_cache
end

def clear_other_cache(id)
  do_the_other_thing
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ignoring the other major problems with this code for now, this is from production code. This code was written with shadowing and exploiting this semantics on purpose, not by accident.&lt;/p&gt;</content:encoded><category>software-design</category><category>ruby</category><category>language-design</category><category>code-readability</category></item><item><title>Misleading Bad Code</title><link>https://juanibiapina.dev/posts/2013-11-11-misleading-bad-code/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2013-11-11-misleading-bad-code/</guid><description>The subconscious burden of bad code.</description><pubDate>Mon, 11 Nov 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Bad code gives me anxiety. Sometimes I even get physically sick. Subtle bad code is even worse.&lt;/p&gt;
&lt;p&gt;The subconscious mind sees and processes lots of things we don&apos;t realize. It communicates it to us in weird ways. Feelings, anxiety, lack of patience are some examples that happen to me, but it&apos;s probably different for each person.&lt;/p&gt;
&lt;p&gt;Let&apos;s look at very simple, unharming code. I have obfuscated the variables, but this is production code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var numberOfUsers = 1, userIsUgly, someController, redefineEverything, t = true, r = someParameter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the premise that less code is better, someone has compiled all variable definitions in one line. Why is this bad?&lt;/p&gt;
&lt;p&gt;Let&apos;s apply the &lt;a href=&quot;/posts/2013-11-01-single-responsibility-principle/&quot;&gt;Single Responsibility Principle&lt;/a&gt;. That means: How many reasons to change does this line have?&lt;/p&gt;
&lt;p&gt;This one line has at least six different reasons to change. When you need to spend a long time (seconds are a long time for the subconscious) reading one line with several responsibilities that might not even interest you at that moment, this process makes your subconscious go crazy. That alone might make me stand up from your computer and get a coffee.&lt;/p&gt;
&lt;p&gt;Later one person refactored that code into this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var numberOfUsers = 1, userIsUgly, someController,
    redefineEverything, t = true, r = someParameter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The line break. Since we&apos;re in 1986, the line break in the bad code was inserted to make the line easier to read. There is no other way to make it easier to read, right? So why not break the one conceptual line into two, and have the reader of the code make the effort to figure out these are in fact the same line?&lt;/p&gt;
&lt;p&gt;Two lines that are in fact the same line. The one thing that points out that they are the same line is the comma in the end of line one. Also, this feature of arbitrarily breaking lines at random points varies by language and by symbol. Some things sometimes allow line breaks, other things sometimes don&apos;t. Imagine a powerful processor (again your subconscious) going through this idea every time you happen to glimpse over this line while trying to read code. Metaphorical subconscious suicide.&lt;/p&gt;
&lt;p&gt;Also, not everyone has screens of the same size, or even the same editors, or even the same window sizes.&lt;/p&gt;
&lt;p&gt;So why not this version?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var numberOfUsers = 1,
    userIsUgly,
    someController,
    redefineEverything,
    t = true,
    r = someParameter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All line breaks are now meaningful.&lt;/p&gt;
&lt;p&gt;You&apos;re saving 3 chars on each line. At what cost? Mental Mapping.&lt;/p&gt;
&lt;p&gt;You&apos;re making the reader of the code who is interested on the definition of &lt;code&gt;someController&lt;/code&gt; look at line 3, then at line one (for the &apos;var&apos; keyword), to see that this is a variable definition. When looking at line one there is the extra burden of removing the other var definition (&lt;code&gt;numberOfUsers&lt;/code&gt;) from the current context (remember I&apos;m talking about subconscious burdens).&lt;/p&gt;
&lt;p&gt;Also in practice you can&apos;t search for &lt;code&gt;var someController&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What about this?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var numberOfUsers = 1
var userIsUgly
var someController
var redefineEverything
var t = true
var r = someParameter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To the subconscious mind, this version might be the most scary. After writing this, it&apos;s easy to see you should do something about it, and might eventually fall back to one of the above. But so far, it highlights the problem the best, and it&apos;s the easier to refactor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.chzbgr.com/maxW500/997281536/hC8F8D378/&quot; alt=&quot;It&amp;#x27;s a tarp&quot;&gt;&lt;/p&gt;
&lt;p&gt;This version has six different lines that do different things. They define variables that are used in different places. If you&apos;re looking for one, you&apos;ll find it in one line. You can safely ignore the other lines. No unnecessary line breaks.&lt;/p&gt;
&lt;p&gt;Arguebly better than all others, but scary anyway. This version points you to several bigger issues. This method is probably doing too many things and it should be refactored. These variable definitions should probably be moved closer to their usages (and you didn&apos;t do it because the method is such a mess that you can&apos;t easily find them, true story). To the experienced programmer, this code is all probably going to be gone after refactoring. There is a lot of work to come.&lt;/p&gt;
&lt;p&gt;Now imagine that on your subconscious mind on a Monday morning. Or a Friday night. Whatever scares you most.&lt;/p&gt;
&lt;p&gt;Point: It&apos;s very hard for your subconscious mind to deal with legacy code where code complexity and major design issues are hidden behind poorly compacted lines and bad refactoring. Undoing one mess reveals a bigger mess and that is frustrating and makes it hard to find the right direction to go.&lt;/p&gt;
&lt;p&gt;Try one of those Chinese puzzles where you have to undo everything before you can solve it.&lt;/p&gt;</content:encoded><category>software-design</category><category>refactoring</category><category>code-readability</category></item><item><title>Single Responsibility Principle</title><link>https://juanibiapina.dev/posts/2013-11-01-single-responsibility-principle/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2013-11-01-single-responsibility-principle/</guid><description>A quick reminder of what is the SRP and writing good code</description><pubDate>Fri, 01 Nov 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A quick &lt;a href=&quot;https://www.google.com/search?q=single+responsibility+principle&amp;#x26;oq=single+resposibility&quot;&gt;google search for single responsibility principle&lt;/a&gt; for me returns these top 5 results, all of which contain this saying in one way or another:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In this context, a responsibility is considered to be one reason to change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the other hand, you&apos;ll see people mostly talking about responsibilities as &quot;things to do&quot;. As an example, check out &lt;a href=&quot;http://stackoverflow.com/questions/1068558/oo-design-in-rails-where-to-put-stuff/1071510#1071510&quot;&gt;this answer on StackOverflow&lt;/a&gt; which is great! but says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A good way to think about it is the &quot;single responsibility principle,&quot; which says that a class should be responsible for a single (or small number) of things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I find this way of thinking misleading.&lt;/p&gt;
&lt;p&gt;In the context of SRP, the word responsibility can not be used interchangeably with &quot;do a thing&quot;. One responsibility is not doing one thing. The reason for this is the definition of thing, which overall, is not agreed upon.&lt;/p&gt;
&lt;p&gt;A &quot;thing&quot; can vary from processor instructions to major business logic. If every person in the project has a different view on how much is one thing (which is very likely), there will never be an agreement on the sizes of classes, henceforth, on their responsibilities. It is simply not viable to have a discussion on how many things a method or class does.&lt;/p&gt;
&lt;p&gt;Reasons to change, on the other hand, is much more practical. If there is more than one reason a class should change, break it in two. This is easy to apply to rails models, views and controllers, but we hardly do. It&apos;s common to write tested rails code that works and goes to production on the same day. It much more sensible, and rather difficult, realize that you&apos;re falling on a trap and need to refactor your code.&lt;/p&gt;
&lt;p&gt;Here is a final note: For a second, forget your framework. Think about how you would do this with another framework. Think about how you would do this in a functional programming language. Consider this, get back and refactor so it reads better and is more easily maintainable.&lt;/p&gt;</content:encoded><category>software-design</category><category>ruby</category><category>refactoring</category><category>code-readability</category></item><item><title>Command line habits</title><link>https://juanibiapina.dev/posts/2012-10-07-console-habits/</link><guid isPermaLink="true">https://juanibiapina.dev/posts/2012-10-07-console-habits/</guid><description>How to check your terminal habits and discover how to be more efficient on the command line.</description><pubDate>Sun, 07 Oct 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Some time ago, my friend Duck wrote a very small shell script to check our command line habits.
Here is my current version, after some modifications.
It uses &lt;code&gt;history&lt;/code&gt; from zsh, so you might need to change it if you&apos;re using bash (with contributions from Bruno Maioli and Serge Gebhardt):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;alias habits=&apos;\history -500 -1 | sed &quot;s/^[[:space:]]*[0-9]*[[:space:]]*//&quot; | sort | uniq -c | sort -n -r | head -n 10&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows me to check what sorts of commands I usually type.
I use this idea to find ways to make my programming environment more efficient.
This is my current habits output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;     174 g s
      37 g a .
      21 g push
      21 g c
      14 guard
      14 g push heroku master
      11 r s
      11 g d
       9 vim
       6 g a -u .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;g&lt;/code&gt; is just an alias for git, and most git commands I use are also aliased on my global config.
&lt;code&gt;r s&lt;/code&gt; (rails server), &lt;code&gt;guard&lt;/code&gt; and &lt;code&gt;vim&lt;/code&gt; are called automatically by tmuxinator when I want to switch projects.&lt;/p&gt;
&lt;p&gt;Every time I check this output I learn something new about my habits.
This time, I&apos;m observing how much git is an integral part of my workflow, and how much time I spend checking statuses and diffs.
It hints me to the need of some sort of visual representation of my working tree, but I still don&apos;t know what to do about that.&lt;/p&gt;
&lt;p&gt;What I may want to do for the next alias version is to filter out git commands.
They will be there anyway, so I might as well consider them &apos;pretty optimised&apos; and move on to the next set.
I suspect a bunch of rake related stuff will come up.&lt;/p&gt;</content:encoded><category>shell</category><category>tooling</category></item></channel></rss>