<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Gustavo.eng.br</title><description>Musings of a bit pusher and AI foreman.</description><link>https://gustavo.eng.br</link><item><title>The best &quot;undocumented&quot; feature of Claude Plugins</title><link>https://gustavo.eng.br/posts/instructions-for-claude-plugins</link><guid isPermaLink="true">https://gustavo.eng.br/posts/instructions-for-claude-plugins</guid><description>Discovering how plugin hooks can inject additional context into Claude Code sessions through the undocumented additionalContext feature</description><pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Note&lt;/h2&gt;
&lt;p&gt;The plugin mentioned below was removed from the repository as there&apos;s now a &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/tree/main/XcodeBuildTools&quot;&gt;better one&lt;/a&gt;. All links  still work but they reference the last version before I removed it from the repository.&lt;/p&gt;
&lt;h2&gt;Getting started with my first plugin&lt;/h2&gt;
&lt;p&gt;When Anthropic &lt;a href=&quot;https://www.anthropic.com/news/claude-code-plugins&quot;&gt;announced Plugins&lt;/a&gt; for Claude Code 3 weeks ago, I immediately wanted to create one. Their &lt;a href=&quot;https://www.anthropic.com/news/skills&quot;&gt;Skills announcement&lt;/a&gt; a week later adding support for them in Plugins sealed the deal.&lt;/p&gt;
&lt;p&gt;My own &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins&quot;&gt;plugin marketplace repository&lt;/a&gt; was born from this excitement, along with my &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/tree/00a680b9b6bcec43fa68cf4b2a1946a10b08553e/SwiftDevelopment&quot;&gt;first plugin&lt;/a&gt; to help with iOS and macOS development. The plugin consisted of some of the MCP servers that I use on all my projects, a simple slash command and, what I was most excited about, a skill to help Claude when dealing with &lt;code&gt;xcodebuild&lt;/code&gt;, a notoriously verbose tool that is known to eat a lot of tokens.&lt;/p&gt;
&lt;p&gt;There are some MCP servers out there to help with that but they still produce a lot of tokens and every once in a while a project that compiles fine in Xcode will fail to compile using the plugin for no particular reason, sending Claude on a wild ride trying to fix non-existing issues.&lt;/p&gt;
&lt;p&gt;A tool called &lt;a href=&quot;https://github.com/ldomaradzki/xcsift&quot;&gt;&lt;code&gt;xcsift&lt;/code&gt;&lt;/a&gt; showed up a few weeks ago. It can parse the verbose output of &lt;code&gt;xcodebuild&lt;/code&gt; and &lt;code&gt;swift&lt;/code&gt; and output a succinct json that is easier for Claude to parse and very token efficient.&lt;/p&gt;
&lt;p&gt;I started using it on some of my projects by adding instructions to my &lt;code&gt;CLAUDE.md&lt;/code&gt; files but I decided to integrate it into a skill within this plugin.&lt;/p&gt;
&lt;h2&gt;Building the Skill&lt;/h2&gt;
&lt;p&gt;For those unfamiliar: a skill is a set of instructions and supporting files and scripts Claude loads on demand. Sessions start with Claude reading a brief description to determine when to activate the skill, then load full instructions only when needed, saving context.&lt;/p&gt;
&lt;p&gt;My skill had what I thought was a simple, straightforward description that I hoped would be enough to make it be used when necessary:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Compiles Xcode projects and swift packages and report errors or warnings.  Use it whenever you would use &lt;code&gt;xcodebuild&lt;/code&gt; or &lt;code&gt;swift&lt;/code&gt; commands.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/tree/00a680b9b6bcec43fa68cf4b2a1946a10b08553e/SwiftDevelopment/skills/swift-compile/SKILL.md&quot;&gt;rest of the skill&lt;/a&gt; instructed Claude to use the included shell script whenever it needed to invoke &lt;code&gt;xcodebuild&lt;/code&gt;. The script was simple: it first makes sure &lt;code&gt;xcsift&lt;/code&gt; is installed on your machine, installing it if necessary. Then it invokes &lt;code&gt;xcodebuild&lt;/code&gt; with all the provided flags and some that I always want to provide.&lt;/p&gt;
&lt;p&gt;Testing the skill locally on one of my projects by asking it to make sure the project compiled worked as I expected:&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background: #000; color: #bbb; padding: 1em; border-radius: 8px; border: 5px solid #888; font-family: Monaco, monospace; font-size: 13px; overflow-x: auto;&quot;&amp;gt;
&amp;lt;p style=&quot;margin: 0; color: #000; background: #bbb; padding: 2px;&quot;&amp;gt;&amp;gt; Make sure the project compiles&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;⏺ I&apos;ll build the project to check for any compilation errors.&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; color: #000; background: #bbb; padding: 2px;&quot;&amp;gt;&amp;gt; The &quot;swift-compile&quot; skill is loading&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;⏺ I&apos;ll build the workspace to check for compilation errors.&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;&amp;lt;span style=&quot;color: #0b0;&quot;&amp;gt;⏺&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color: #fff;&quot;&amp;gt;Bash&amp;lt;/span&amp;gt;(cd /Users/gustavoambrozio/Development/ClaudeCodePlugins/SwiftDevelopment/skills/swift-compile &amp;amp;&amp;amp; scripts/xcodebuild.sh -workspace /Users/gustavoambrozio/Develop…) &amp;lt;span style=&quot;color: #555;&quot;&amp;gt;timeout: 5m 0s&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;  ⎿  {&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;     &quot;status&quot; : &quot;success&quot;,&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;     &quot;summary&quot; : {&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; color: #515151;&quot;&amp;gt;   … +5 lines (ctrl+o to expand)&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;  ⎿  &amp;lt;span style=&quot;color: #b00;&quot;&amp;gt;Shell cwd was reset to /Users/gustavoambrozio/Development/MacOSTestProject&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;&amp;lt;span style=&quot;color: #fff;&quot;&amp;gt;⏺ ✅ Project compiles successfully!&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;  The build completed with:&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; color: #fff;&quot;&amp;gt;  - 0 errors&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;  - &amp;lt;span style=&quot;color: #fff;&quot;&amp;gt;1 warning&amp;lt;/span&amp;gt; (minor)&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;  - Build status: &amp;lt;span style=&quot;color: #fff;&quot;&amp;gt;success&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0; min-height: 17px;&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;margin: 0;&quot;&amp;gt;  The MacOSTestProject workspace is building correctly. The single warning is minor and doesn&apos;t prevent compilation.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;Good sign. I published and asked my co-workers to test it out.&lt;/p&gt;
&lt;p&gt;The feedback was quick and disappointing: Claude was still using &lt;code&gt;xcodebuild&lt;/code&gt; directly and that they had to explicitly ask Claude to use the skill.&lt;/p&gt;
&lt;p&gt;To try to reproduce the issue I changed my agent to Haiku and turned off thinking mode. This led to Claude ignoring my instructions now.&lt;/p&gt;
&lt;p&gt;I sighed and put this aside for a while until something caught my attention.&lt;/p&gt;
&lt;h2&gt;How a deprecation warning revealed the solution&lt;/h2&gt;
&lt;p&gt;Later that day I opened Claude Code and saw a note saying that &quot;Output styles are deprecated&quot;. This got me upset as this is a feature I cherish (more on that in a future post).&lt;/p&gt;
&lt;p&gt;Luckily it provided an alternative in the form of a plugin hook.&lt;/p&gt;
&lt;p&gt;Claude shows a link to their &lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/explanatory-output-style&quot;&gt;Explanatory Output Style Plugin&lt;/a&gt;. This plugin is very simple, consisting only of a &lt;code&gt;SessionStart&lt;/code&gt; hook that gives initial instructions to Claude Code.&lt;/p&gt;
&lt;p&gt;The plugin invokes this simple script when a session starts:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

# Output the explanatory mode instructions as additionalContext
# This mimics the deprecated Explanatory output style

cat &amp;lt;&amp;lt; &apos;EOF&apos;
{
  &quot;hookSpecificOutput&quot;: {
    &quot;hookEventName&quot;: &quot;SessionStart&quot;,
    &quot;additionalContext&quot;: &quot;You are in &apos;explanatory&apos; output style mode, where you should provide educational insights about the codebase as you help with the user&apos;s task.\n\nYou should be clear and educational, providing helpful explanations while remaining focused on the task. Balance educational content with task completion. When providing insights, you may exceed typical length constraints, but remain focused and relevant.\n\n## Insights\nIn order to encourage learning, before and after writing code, always provide brief educational explanations about implementation choices using (with backticks):\n\&quot;`★ Insight ─────────────────────────────────────`\n[2-3 key educational points]\n`─────────────────────────────────────────────────`\&quot;\n\nThese insights should be included in the conversation, not in the codebase. You should generally focus on interesting insights that are specific to the codebase or the code you just wrote, rather than general programming concepts. Do not wait until the end to provide insights. Provide them as you write code.&quot;
  }
}
EOF

exit 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out plugins &lt;em&gt;DO&lt;/em&gt; have a way to provide instructions to the user by using a &lt;code&gt;SessionStart&lt;/code&gt; hook. While saying this feature is &quot;&lt;em&gt;undocumented&lt;/em&gt;&quot; is not accurate - &lt;a href=&quot;https://docs.claude.com/en/docs/claude-code/hooks#sessionstart-decision-control&quot;&gt;the documentation for hooks&lt;/a&gt; clearly shows that you can do this - I hadn&apos;t considered using this in the context of plugins to provide plugin specific instructions.&lt;/p&gt;
&lt;p&gt;This led me to create a &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/blob/16df24ccde19476fa661759443aed2017d008b6f/SwiftDevelopment/hooks/session-start.py&quot;&gt;similar script&lt;/a&gt; and added to my plugin&apos;s hook configuration. I don&apos;t like the way Anthropic embeds their instructions in a heredoc on the shell script so my plugin reads &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/tree/16df24ccde19476fa661759443aed2017d008b6f/SwiftDevelopment/hooks/session-start.md&quot;&gt;a markdown&lt;/a&gt; of the instructions so it&apos;s easier to edit them.&lt;/p&gt;
&lt;p&gt;And that got me thinking that I could use hooks to make &lt;em&gt;extra&lt;/em&gt; sure that Claude would use my scripts to compile code.&lt;/p&gt;
&lt;p&gt;Since Claude always sends a &lt;code&gt;PreToolUse&lt;/code&gt; hook that you can use to deny the use of a tool I created &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/tree/00a680b9b6bcec43fa68cf4b2a1946a10b08553e/SwiftDevelopment/hooks/pre-tool-use.py&quot;&gt;another script&lt;/a&gt; and added to the &lt;code&gt;PreToolUse&lt;/code&gt; hook configuration. It checks what command line is being called and if the user is using &lt;code&gt;xcodebuild&lt;/code&gt; directly it denies access to the tool and clearly tells Claude to use the skill.&lt;/p&gt;
&lt;p&gt;After all this arm twisting I&apos;m pretty confident that Claude will always use my skill to compile my projects. :crossed_fingers:&lt;/p&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Plugins are a powerful tool for Claude Code and I think we&apos;ll start seeing a lot of very useful ones in the next few weeks, especially when developers start to understand and explore the interactions between all the parts of a plugin. I&apos;m excited to expand this plugin with more tools and create others.&lt;/p&gt;
&lt;p&gt;If you have any issues with this plugin or want to improve it in any way, please &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/issues&quot;&gt;open an issue&lt;/a&gt; or, even better, clone and &lt;a href=&quot;https://github.com/gpambrozio/ClaudeCodePlugins/pulls&quot;&gt;create a PR&lt;/a&gt;.&lt;/p&gt;
</content:encoded><author>Gustavo Ambrozio</author></item><item><title>Open Sourcing the Claude Settings MacOS app</title><link>https://gustavo.eng.br/posts/claude-settings-open-sourced</link><guid isPermaLink="true">https://gustavo.eng.br/posts/claude-settings-open-sourced</guid><description>Claude Code settings can get messy with lots of projects. ClaudeSettings can help.</description><pubDate>Sun, 23 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;How Claude Code Changed My Workflow&lt;/h2&gt;
&lt;p&gt;For most of my professional life as a developer I&apos;ve had ideas of apps I could build to solve particular problems or just improve parts of my life. But almost all of them were just relegated to notes or even just dismissed outright as I knew I didn&apos;t have time to work on them.&lt;/p&gt;
&lt;p&gt;Then I started using Claude Code and in a few sessions I saw how it could be used to build things much faster than I could, especially for prototypes and experiments where quick iteration mattered more than code quality.&lt;/p&gt;
&lt;p&gt;Ideas that had been relegated to notes quickly turned into projects and I soon had a few new folders full of code. I also added Claude to a lot of old projects and even to my note taking folder itself. But this rapid expansion created a new problem.&lt;/p&gt;
&lt;h2&gt;Managing Claude Code Settings Across Projects&lt;/h2&gt;
&lt;p&gt;With every project I had a set of MCP servers, &lt;a href=&quot;https://code.claude.com/docs/en/slash-commands&quot;&gt;slash commands&lt;/a&gt;, different authorization settings. Then Anthropic implemented &lt;a href=&quot;https://code.claude.com/docs/en/sub-agents&quot;&gt;sub-agents&lt;/a&gt;, &lt;a href=&quot;https://code.claude.com/docs/en/plugins&quot;&gt;plugins&lt;/a&gt;, &lt;a href=&quot;https://code.claude.com/docs/en/output-styles&quot;&gt;output styles&lt;/a&gt;, &lt;a href=&quot;https://code.claude.com/docs/en/skills&quot;&gt;skills&lt;/a&gt; and before I knew it, I didn&apos;t know what I had set up for each project. Copy/pasting of json from one folder to another was a common operation. This manual management was tedious and error-prone, exactly the kind of problem that deserved a better solution.&lt;/p&gt;
&lt;p&gt;That&apos;s when I decided to create the &lt;a href=&quot;https://github.com/gpambrozio/ClaudeSettings&quot;&gt;ClaudeSettings&lt;/a&gt; MacOS app to try to make this new problem easier to deal with.&lt;/p&gt;
&lt;p&gt;Two weeks after starting the project, it&apos;s ready to be released and open sourced. It&apos;s mostly built by Claude Code, with some manual refinements here and there. I&apos;m sure there are some edge case bugs out there but it works well enough to make my life a bit easier.&lt;/p&gt;
&lt;h2&gt;How it helps&lt;/h2&gt;
&lt;p&gt;One of my main uses of the app is for &lt;a href=&quot;https://code.claude.com/docs/en/plugins&quot;&gt;plugins&lt;/a&gt;. When installing a plugin in Claude Code, it defaults to installing it in the global configuration and there&apos;s no flag to change this scope (unlike when adding an MCP server). ClaudeSettings makes it easy to move a plugin from the global configuration to a specific project and then copy it only to the projects where you&apos;ll actually use it.&lt;/p&gt;
&lt;p&gt;I also use it to move settings from &lt;code&gt;settings.local.json&lt;/code&gt; to the &lt;code&gt;settings.json&lt;/code&gt; file that gets checked into Git. Or to move settings I want on all my projects to the global config. All with just a simple drag and drop.&lt;/p&gt;
&lt;p&gt;There are still many improvements to make to the app, like supporting MCP server configurations, slash commands, sub-agents and who knows what else Anthropic will add to Claude Code by next week. Just keeping up with &lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md&quot;&gt;their changelog&lt;/a&gt; is almost a full time job.&lt;/p&gt;
&lt;p&gt;So if you also have a ton of projects and want to tame your settings, give it a try. The app is open source on GitHub. Contributions and feedback welcome as Claude Code continues to evolve.&lt;/p&gt;
&lt;p&gt;Here&apos;s a little taste of it:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/ClaudeSettings.png&quot; alt=&quot;ClaudeSettings app interface&quot; /&gt;&lt;/p&gt;
</content:encoded><author>Gustavo Ambrozio</author></item><item><title>How I&apos;m still using my Claude subscription for unattended jobs</title><link>https://gustavo.eng.br/posts/claude-autonomy</link><guid isPermaLink="true">https://gustavo.eng.br/posts/claude-autonomy</guid><description>How to avoid paying for API pricing when running claude unattended</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently Anthropic changed their policy to make using &lt;code&gt;claude -p&lt;/code&gt; (the headless / print mode that almost every Claude Code automation I&apos;ve written depends on) draw from API credits instead of my subscription usage limits. Interactive Claude Code stays on the subscription I already pay for; only &lt;code&gt;-p&lt;/code&gt; gets the per-token bill.
&lt;a href=&quot;https://code.claude.com/docs/en/agent-sdk/overview&quot;&gt;&lt;img src=&quot;./images/003_policy_change.png&quot; alt=&quot;Policy change&quot; /&gt;&lt;/a&gt;
Needless to say, &lt;a href=&quot;https://support.claude.com/en/articles/15036540-use-the-claude-agent-sdk-with-your-claude-plan&quot;&gt;despite their attempt to make this sound like a good thing&lt;/a&gt;, I was not very happy with this change and was not about to give up my VC-backed subsidized tokens just yet. So I started thinking about how I could keep all my automations while still not having to pay this new &lt;code&gt;-p&lt;/code&gt; tax.&lt;/p&gt;
&lt;p&gt;I was pretty sure I could accomplish the same as &lt;code&gt;-p&lt;/code&gt; with an &quot;interactive&quot; session, a few hooks, and some tmux trickery.&lt;/p&gt;
&lt;p&gt;After a few attempts I had a &lt;a href=&quot;https://github.com/gpambrozio/Autonomy&quot;&gt;Claude Code plugin and a couple of wrapper scripts&lt;/a&gt; that drive a normal interactive Claude session as if there were a human at the keyboard.&lt;/p&gt;
&lt;h2&gt;TL;DR: How can you use this&lt;/h2&gt;
&lt;p&gt;If all you need is to replace your use of &lt;code&gt;claude -p&lt;/code&gt;, then the &lt;a href=&quot;https://github.com/gpambrozio/Autonomy/tree/main/bin/claude-auto&quot;&gt;&lt;code&gt;claude-auto&lt;/code&gt;&lt;/a&gt; script can probably replace it with no changes. Just download it (and &lt;a href=&quot;https://github.com/gpambrozio/Autonomy/tree/main/bin/claude-transcript&quot;&gt;&lt;code&gt;claude-transcript&lt;/code&gt;&lt;/a&gt;), put them somewhere in your &lt;code&gt;$PATH&lt;/code&gt;, and use &lt;code&gt;claude-auto&lt;/code&gt; wherever you used &lt;code&gt;claude -p&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, one of my scripts was just &lt;code&gt;claude -p --dangerously-skip-permissions &quot;/pr-comment&quot;&lt;/code&gt;. &lt;code&gt;pr-comment&lt;/code&gt; is one of my skills that runs a code review on the current GitHub PR for the branch it is running on and adds comments to it. For this one I replaced it with &lt;code&gt;claude-auto --dangerously-skip-permissions &quot;/pr-comment&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The output changes a bit but, IMHO, in a good way. &lt;code&gt;claude-auto&lt;/code&gt; gives you a simple transcript of what Claude did during your session in a way that was hard to get with &lt;code&gt;claude -p&lt;/code&gt;. This comes from &lt;a href=&quot;https://github.com/gpambrozio/Autonomy/tree/main/bin/claude-transcript&quot;&gt;&lt;code&gt;claude-transcript&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Look at the &lt;a href=&quot;https://github.com/gpambrozio/Autonomy/tree/main/examples&quot;&gt;examples&lt;/a&gt; folder for sample scripts - cron, batch, parallel-across-repos, and an attended variant for the rare cases where you do want Claude to be able to ask you a question.&lt;/p&gt;
&lt;h2&gt;How does it work&lt;/h2&gt;
&lt;p&gt;A Claude Code plugin is a directory with a collection of hooks, skills, agents, etc. Autonomy is four small hook handlers.&lt;/p&gt;
&lt;h3&gt;SessionStart: tell Claude to act without user input&lt;/h3&gt;
&lt;p&gt;One of the main uses of a &lt;code&gt;SessionStart&lt;/code&gt; hook is to inject context at the start of a session. Autonomy injects a short prompt to steer Claude into working without user input. By default it assumes nobody is watching:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You are running in fully unattended mode. There is no human available to
answer questions, confirm decisions, or approve plans.

Do NOT use the `AskUserQuestion` tool — it is blocked in this mode and
will return a stock denial telling you to proceed without asking. Do not
enter plan mode to seek approval, and do not stop to request clarification.

When requirements are ambiguous, make a reasonable judgement call based on
the existing code, project conventions, and the task description, then
continue. State any non-obvious assumptions you made in your final output
so they can be reviewed after the session ends.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do plan to attach to the tmux session and want to answer the occasional question, set &lt;code&gt;CLAUDE_AUTO_QUESTIONS_OK=1&lt;/code&gt; and Autonomy injects the softer version instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You should work as autonomously as possible on this session so there&apos;s no
need to get into plan mode to create a plan and ask the user for approval.

If you deem that the work cannot be done without user input then you can
ask questions. Use the `AskUserQuestion` tool for this. But only do this
if it is absolutely necessary, otherwise just do the work without user
input.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;PreToolUse: actually blocking questions&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;SessionStart&lt;/code&gt; prompt is just a hint. Claude sometimes decides this is the absolutely-necessary case and reaches for &lt;code&gt;AskUserQuestion&lt;/code&gt; anyway. In an unattended run &quot;you should not ask&quot; needs to become &quot;you cannot ask&quot;, otherwise the session sits there forever waiting for an answer that will never come.&lt;/p&gt;
&lt;p&gt;The fix is a &lt;code&gt;PreToolUse&lt;/code&gt; hook with &lt;code&gt;&quot;matcher&quot;: &quot;AskUserQuestion&quot;&lt;/code&gt;. When the hook prints this JSON to stdout, Claude Code refuses to run the tool and surfaces &lt;code&gt;permissionDecisionReason&lt;/code&gt; to the model in place of a result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hookSpecificOutput&quot;: {
    &quot;hookEventName&quot;: &quot;PreToolUse&quot;,
    &quot;permissionDecision&quot;: &quot;deny&quot;,
    &quot;permissionDecisionReason&quot;: &quot;This session is running in autonomous mode, so no human is available to answer. Use your best judgement based on the existing code and the original task description, and continue. State any non-obvious assumptions in your final output for later review.&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The hook reads &lt;code&gt;CLAUDE_AUTO_QUESTIONS_OK&lt;/code&gt; from the environment. If it&apos;s &lt;code&gt;1&lt;/code&gt;, the script exits 0 with no output (allow); otherwise it prints the deny JSON. The same env var is what swaps the SessionStart prompt between the two variants above, so the model and the enforcement layer stay consistent.&lt;/p&gt;
&lt;p&gt;The default is strict. Autonomy assumes nobody is watching. If you intend to attach to the tmux session and want to be pinged for ambiguous calls, &lt;code&gt;CLAUDE_AUTO_QUESTIONS_OK=1 claude-auto &quot;...&quot;&lt;/code&gt; flips both layers to the soft version. &lt;a href=&quot;https://github.com/gpambrozio/Autonomy/blob/main/examples/attended-run.sh&quot;&gt;&lt;code&gt;examples/attended-run.sh&lt;/code&gt;&lt;/a&gt; is a thin wrapper that sets it for you.&lt;/p&gt;
&lt;h3&gt;Stop: when stop doesn&apos;t always mean stop&lt;/h3&gt;
&lt;p&gt;The first version of this hook was dead simple: &lt;code&gt;tmux send-keys /exit&lt;/code&gt;. Every time Stop fired, the session ended because I naively thought that Stop meant the agent was done working. Not really.&lt;/p&gt;
&lt;p&gt;In many situations where Claude needs to wait for some long-running task, it can start a background task or a subagent to do that. In these cases it does fire a &lt;code&gt;Stop&lt;/code&gt; hook, but it says it is waiting for something to finish. When that happened, my script would &quot;type&quot; &lt;code&gt;/exit&lt;/code&gt; and the agent would ask for confirmation before exiting, but there was nothing to confirm or deny, so the flow just broke.&lt;/p&gt;
&lt;p&gt;So what I did instead of just exiting was to ask Claude. My &lt;code&gt;Stop&lt;/code&gt; hook would type a question: &lt;em&gt;&quot;If there are no background jobs you&apos;re waiting for and you&apos;re done working reply with &apos;done&apos; only. If you are waiting for background jobs reply with &apos;waiting&apos; only.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The Stop hook input JSON includes a &lt;code&gt;last_assistant_message&lt;/code&gt; field with the text of Claude&apos;s final reply for that turn. The hook reads it and routes on its content:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&quot;done&quot;&lt;/code&gt; → really done, send &lt;code&gt;/exit&lt;/code&gt; to end the session.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;waiting&quot;&lt;/code&gt; → tool or subagent still in flight; do nothing, the agent will trigger a new &lt;code&gt;Stop&lt;/code&gt; when whatever it&apos;s running ends.&lt;/li&gt;
&lt;li&gt;anything else → ask again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first time a session stops, &lt;code&gt;last_assistant_message&lt;/code&gt; is whatever Claude was saying (usually a summary of work) so we nudge. Claude reads the nudge, classifies its own state, replies with a single word. That reply triggers another Stop. That second Stop&apos;s &lt;code&gt;last_assistant_message&lt;/code&gt; is &lt;code&gt;&quot;done&quot;&lt;/code&gt; or &lt;code&gt;&quot;waiting&quot;&lt;/code&gt;, and the hook acts on it.&lt;/p&gt;
&lt;h3&gt;StopFailure: retry with a counter&lt;/h3&gt;
&lt;p&gt;Just as I was testing the script, Claude started having API issues and returning a bunch of server errors. Kinda lucky in a weird way, as it exposed a potential issue with this approach.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;StopFailure&lt;/code&gt; is fired when a turn ends because of an API error. We can&apos;t tell the difference between recoverable and non-recoverable failures from inside the hook, so we retry up to a limit and then give up.&lt;/p&gt;
&lt;p&gt;The handler keeps a counter file at &lt;code&gt;$TMPDIR/&amp;lt;session-id&amp;gt;&lt;/code&gt;. On each failure it increments. For the first five failures it types &lt;code&gt;Up&lt;/code&gt; + &lt;code&gt;Enter&lt;/code&gt; into the tmux pane after a ten-second pause. &lt;code&gt;Up&lt;/code&gt; recalls the previous prompt in Claude&apos;s history and &lt;code&gt;Enter&lt;/code&gt; re-submits it. On the sixth, it removes the counter and types &lt;code&gt;/exit&lt;/code&gt;. Five retries with a 10s baseline have been enough to ride out every transient I&apos;ve hit; if you have a permanently broken auth or billing setup, you&apos;ll burn through five retries and then exit, which is probably better than what &lt;code&gt;claude -p&lt;/code&gt; would do in this situation.&lt;/p&gt;
&lt;h2&gt;claude-auto&lt;/h2&gt;
&lt;p&gt;Plugins are supposed to be installed in your agent&apos;s configuration, but Autonomy is different: it is only needed in targeted situations. &lt;code&gt;claude-auto&lt;/code&gt; handles that, plus a few other useful things.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;claude-auto&lt;/code&gt; does four things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generates a fresh UUID for &lt;code&gt;--session-id&lt;/code&gt;, so the transcript can be retrieved later.&lt;/li&gt;
&lt;li&gt;Loads the plugin via &lt;code&gt;--plugin-url https://github.com/gpambrozio/Autonomy/archive/refs/heads/main.zip&lt;/code&gt; so there&apos;s no install step for the user. Claude Code fetches the zip on the fly.&lt;/li&gt;
&lt;li&gt;Routes stdin/stdout/stderr so that the session renders on the terminal even when the caller piped us into &lt;code&gt;tee&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Also, after the session ends it pipes the whole thing through &lt;code&gt;claude-transcript&lt;/code&gt; so you have something to scroll back through (or, with &lt;code&gt;--log &amp;lt;file&amp;gt;&lt;/code&gt;, written to a file).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;claude-auto&lt;/code&gt; itself doesn&apos;t know about &lt;code&gt;CLAUDE_AUTO_QUESTIONS_OK&lt;/code&gt;. It just doesn&apos;t strip the environment, so whatever you export reaches the hooks.&lt;/p&gt;
&lt;p&gt;The typical invocation is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;claude-auto &quot;do a thing&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also add any other flags you&apos;d pass to &lt;code&gt;claude&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;claude-auto --dangerously-skip-permissions --effort max --model opus &quot;/github-issue $1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Getting Claude Code to render when stdout is piped&lt;/h2&gt;
&lt;p&gt;This was a bit tricky. In some of my scripts I was capturing the output of &lt;code&gt;claude -p&lt;/code&gt; into a log file, so when I replaced it with &lt;code&gt;claude-auto&lt;/code&gt; I did this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;claude-auto &quot;...&quot; | tee log.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Claude&apos;s stdout is the pipe to &lt;code&gt;tee&lt;/code&gt;, not a TTY. When Claude detects that stdout isn&apos;t a TTY, it falls back to plain-text non-interactive output, which is the same as &lt;code&gt;-p&lt;/code&gt; and would probably charge you accordingly.&lt;/p&gt;
&lt;p&gt;Ironically, I used Claude to figure out how to get itself to still behave as if it were in a TTY even though I was piping stdout.&lt;/p&gt;
&lt;p&gt;As with many shell tricks Claude knows, I don&apos;t fully understand how this works. Claude&apos;s explanation is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the realistic call shapes, &lt;strong&gt;stdin is still a TTY even when stdout is piped&lt;/strong&gt;. The shell only redirects what the pipe captures. If you run &lt;code&gt;cmd | tee log&lt;/code&gt;, the pipe attaches to &lt;code&gt;cmd&lt;/code&gt;&apos;s stdout; the parent shell&apos;s stdin (and &lt;code&gt;cmd&lt;/code&gt;&apos;s stdin, by inheritance) is still your terminal. So I just dup the inherited stdin into stdout and stderr:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;if [ -t 0 ] &amp;amp;&amp;amp; [ -t 1 ] &amp;amp;&amp;amp; [ -t 2 ]; then
    claude ... &quot;$@&quot;             # nothing to fix
elif [ -t 0 ]; then
    claude ... &quot;$@&quot; 1&amp;gt;&amp;amp;0 2&amp;gt;&amp;amp;0   # stdin is a TTY, dup it into the others
else
    claude ... &quot;$@&quot;             # no TTY anywhere, let claude decide
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;1&amp;gt;&amp;amp;0 2&amp;gt;&amp;amp;0&lt;/code&gt; makes fd 1 and fd 2 dups of fd 0. They share the same open file description as the inherited stdin, which is a normal pty fd opened &lt;code&gt;O_RDWR&lt;/code&gt; by the shell. Bun is happy, the TUI renders on the actual terminal, the pipe still gets whatever &lt;code&gt;claude-auto&lt;/code&gt; writes to stdout after Claude exits, which is the transcript dump.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;claude-transcript&lt;/h2&gt;
&lt;p&gt;I wanted a way to get a transcript of the session afterwards, just in case something went wrong or to be able to see what it did.&lt;/p&gt;
&lt;p&gt;There are many projects out there that do that, but I had Claude whip up a simple script.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;claude-transcript&lt;/code&gt; is about 100 lines of Python that walks the JSONL file Claude saves for every session and prints a chronological list of just the parts I care about:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## [1] 2026-05-14 09:12:03 — USER
Implement the new caching layer described in issue #42.

## [2] 2026-05-14 09:12:18 — CLAUDE
I&apos;ll start by reading the existing cache implementation and the issue
description to understand the requirements.

## [3] 2026-05-14 09:15:47 — CLAUDE
Implementation done. Tests pass. PR opened at #167.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;claude-auto&lt;/code&gt; calls it on every exit, so every session ends with a self-contained log on stdout (or in the file you passed to &lt;code&gt;--log&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Limitations / caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tmux required.&lt;/strong&gt; The whole mechanism is &lt;code&gt;tmux send-keys&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;StopFailure&lt;/code&gt; retries everything.&lt;/strong&gt; It can&apos;t distinguish a 429 from a 401, so a session with broken auth will retry five times before exiting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No questions by default.&lt;/strong&gt; Unattended runs deny &lt;code&gt;AskUserQuestion&lt;/code&gt; at the PreToolUse hook. If your automation depends on a human being there to answer, set &lt;code&gt;CLAUDE_AUTO_QUESTIONS_OK=1&lt;/code&gt; and attach to the tmux session.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It&apos;s a workaround.&lt;/strong&gt; Anthropic could try to detect tricks like this and treat them as &lt;code&gt;-p&lt;/code&gt;. My guess is that all these subscriptions will cease to exist or start to get severely limited anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Plugin repo: &lt;a href=&quot;https://github.com/gpambrozio/Autonomy&quot;&gt;github.com/gpambrozio/Autonomy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Drop &lt;code&gt;bin/claude-auto&lt;/code&gt; and &lt;code&gt;bin/claude-transcript&lt;/code&gt; on your &lt;code&gt;$PATH&lt;/code&gt;, install tmux, start a tmux session, and run &lt;code&gt;claude-auto &quot;do a thing&quot;&lt;/code&gt;. The plugin is fetched from GitHub on every invocation; there&apos;s no install step beyond that.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Gustavo Ambrozio</author></item></channel></rss>