<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Henry Godnick</title>
    <description>The latest articles on DEV Community by Henry Godnick (@godnick).</description>
    <link>https://dev.to/godnick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3817679%2Fcc7b25b9-6c16-4824-b743-b929b96b2192.png</url>
      <title>DEV Community: Henry Godnick</title>
      <link>https://dev.to/godnick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/godnick"/>
    <language>en</language>
    <item>
      <title>Claude Pro vs Max vs API for Claude Code: which plan fits your actual usage</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:02:55 +0000</pubDate>
      <link>https://dev.to/godnick/claude-pro-vs-max-vs-api-for-claude-code-which-plan-fits-your-actual-usage-59c</link>
      <guid>https://dev.to/godnick/claude-pro-vs-max-vs-api-for-claude-code-which-plan-fits-your-actual-usage-59c</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I build TokenBar, a Mac menu bar app that tracks AI usage and cost. It gets one mention near the end. The rest uses only Claude Code's own files and Anthropic's published prices.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"Should I upgrade to Max?" is usually answered by gut feel: you hit a limit twice in one week and start shopping. A better answer is already on your disk. Claude Code keeps a transcript of every session, and every model response in it records its token counts. Price those tokens at API rates and you get a real number to set beside each plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're choosing between
&lt;/h2&gt;

&lt;p&gt;From Anthropic's &lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; and the &lt;a href="https://support.claude.com/en/articles/11049741-what-is-the-max-plan" rel="noopener noreferrer"&gt;Max plan help article&lt;/a&gt;, as of October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$20/mo, or $17/mo billed yearly ($200 up front)&lt;/td&gt;
&lt;td&gt;At least 5x Free per 5-hour session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max 5x&lt;/td&gt;
&lt;td&gt;$100/mo, monthly only&lt;/td&gt;
&lt;td&gt;5x Pro per session, plus $100/mo in API credits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max 20x&lt;/td&gt;
&lt;td&gt;$200/mo, monthly only&lt;/td&gt;
&lt;td&gt;20x Pro per session, plus $200/mo in API credits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API (Console)&lt;/td&gt;
&lt;td&gt;Pay per token&lt;/td&gt;
&lt;td&gt;No plan limits, only rate limits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Some details matter more than the price:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limits aren't in dollars.&lt;/strong&gt; Every plan has a rolling 5-hour session window, and paid plans add a weekly limit. Anthropic says there's no fixed message count, because usage depends on context length, model and features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's one shared pool.&lt;/strong&gt; Claude Code, claude.ai, desktop and mobile all draw from the same allowance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When you hit a limit&lt;/strong&gt;, you can wait, upgrade, or turn on usage credits and keep going at standard API rates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The API prices you'll use
&lt;/h2&gt;

&lt;p&gt;Per million tokens, from the &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;API pricing docs&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;5-min cache write&lt;/th&gt;
&lt;th&gt;1-hour cache write&lt;/th&gt;
&lt;th&gt;Cache read&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5.5&lt;/td&gt;
&lt;td&gt;$4&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;$8&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;td&gt;$4&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haiku 5.5 (prompts up to 100k)&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;$0.125&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;td&gt;$0.01&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haiku 5.5 (prompts over 100k)&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;$0.625&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cache reads are where most Claude Code tokens land. A long session re-sends its whole history on every request, and caching is the reason that's affordable. One detail affects the comparison: per the &lt;a href="https://code.claude.com/docs/en/costs" rel="noopener noreferrer"&gt;Claude Code cost docs&lt;/a&gt;, the cache lifetime is an hour on a subscription but five minutes by default on an API key. So the same work can cost a bit more on the API, because you rebuild the cache more often after short breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the numbers live
&lt;/h2&gt;

&lt;p&gt;Claude Code writes transcripts as JSONL to &lt;code&gt;~/.claude/projects/&amp;lt;project-dir&amp;gt;/&amp;lt;session-id&amp;gt;.jsonl&lt;/code&gt;, and keeps them for 30 days by default (&lt;code&gt;cleanupPeriodDays&lt;/code&gt;). Assistant lines carry a &lt;code&gt;message.usage&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_creation_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20780&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_read_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15718&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1101&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two caveats before you trust a script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic calls the format internal&lt;/strong&gt;, so it can change in any release. For anything long-lived, the &lt;a href="https://code.claude.com/docs/en/sessions" rel="noopener noreferrer"&gt;sessions docs&lt;/a&gt; point you to &lt;code&gt;claude -p --output-format json&lt;/code&gt;, which returns usage and cost for a run, or to &lt;code&gt;/export&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One response can span several lines.&lt;/strong&gt; Claude Code may write one line per content block, each repeating the same &lt;code&gt;message.id&lt;/code&gt; and usage. If you sum every line you'll overcount, so dedupe by &lt;code&gt;message.id&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A small script to price your last 30 days
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
# claude_cost.py: API-equivalent cost of local Claude Code sessions
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;

&lt;span class="c1"&gt;# $ per million tokens: input, 5m write, 1h write, cache read, output
&lt;/span&gt;&lt;span class="n"&gt;PRICES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sonnet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;haiku&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.125&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# prompts &amp;lt;= 100k
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;HAIKU_LONG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.625&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# prompts &amp;gt; 100k
&lt;/span&gt;
&lt;span class="n"&gt;msgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.claude/projects/**/*.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ignore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uuid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;fam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PRICES&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;fam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;  &lt;span class="c1"&gt;# unknown or synthetic model entries
&lt;/span&gt;    &lt;span class="n"&gt;inp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_read_input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_creation_input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;split&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_creation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;w1h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral_1h_input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;w5m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cw&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;w1h&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PRICES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fam&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fam&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;haiku&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;inp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cw&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HAIKU_LONG&lt;/span&gt;
    &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inp&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;w5m&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;w1h&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cr&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1e6&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;8.2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Active days: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  Total: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Avg per active day: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;python3 claude_cost.py&lt;/code&gt;. It maps models by family name, so check the table against the pricing page whenever Anthropic changes prices. Older model versions are priced differently. Opus 4.8, for example, is $5 in and $25 out.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;**&lt;/code&gt; glob also picks up subagent transcripts if your version writes them to subfolders. You want those included, because subagent requests count toward your usage too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the result
&lt;/h2&gt;

&lt;p&gt;Treat the number as "what this would have cost on the API," not "how much of my plan I used." Anthropic doesn't publish a dollar equivalent for plan allowances, so no exact break-even point exists. These are the comparisons that do hold up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monthly API-equivalent below $20.&lt;/strong&gt; Pay-as-you-go in the Console may cost you less than Pro. You lose claude.ai chat, and the cache expires sooner, but if you mostly use Claude Code a few days a month, it's worth a look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well above $20, but you rarely see a limit message.&lt;/strong&gt; Pro is doing its job. Upgrading buys headroom you don't use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You hit the 5-hour or weekly limit every week.&lt;/strong&gt; Open &lt;code&gt;/usage&lt;/code&gt;. On paid plans it shows your plan bars and a breakdown by skills, subagents, plugins and MCP servers, and it flags any behavior (long context, cache misses) that makes up 10% or more of your usage. Press &lt;code&gt;d&lt;/code&gt; or &lt;code&gt;w&lt;/code&gt; to switch between the last day and week. Fix what it flags first. If you still hit the wall, Max 5x is the next step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You're comparing Max with the API.&lt;/strong&gt; Max includes $100 or $200 a month in API credits on top of your plan usage, so count those if you'd use them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a reference point, Anthropic's cost docs say enterprise Claude Code deployments average about &lt;strong&gt;$13 per developer per active day&lt;/strong&gt; and &lt;strong&gt;$150â€“250 per developer per month&lt;/strong&gt;, with 90% of users under $30 per active day. Those are API-rate figures for heavy, full-time use. If your average per active day is close to that, you're a normal heavy user, not an outlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the right &lt;code&gt;/usage&lt;/code&gt; number
&lt;/h2&gt;

&lt;p&gt;The Session block at the top of &lt;code&gt;/usage&lt;/code&gt; shows a dollar cost calculated locally at list price. Anthropic says it's meant for API users. On Pro or Max it doesn't affect your bill, and it resets on &lt;code&gt;/clear&lt;/code&gt;. It's still useful as a per-task price tag. Run &lt;code&gt;/clear&lt;/code&gt;, do one task, then check &lt;code&gt;/usage&lt;/code&gt;, and you'll know what that kind of task costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check again after a month
&lt;/h2&gt;

&lt;p&gt;One month of data beats any blog post's rule of thumb, mine included. Run the script at the end of each month (transcripts expire after 30 days unless you change &lt;code&gt;cleanupPeriodDays&lt;/code&gt;), and compare it with how often you hit a limit.&lt;/p&gt;

&lt;p&gt;I wanted that number visible without running a script, so I built &lt;a href="https://tokenbar.site" rel="noopener noreferrer"&gt;TokenBar&lt;/a&gt;, which shows Claude, Codex and other providers' usage and cost in the Mac menu bar. The script above gives you the same core number for free.&lt;/p&gt;

&lt;p&gt;What does your average active day come out to? I'm curious how far apart people's numbers are.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How Mac apps add text suggestions to every app: the Accessibility API explained</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Tue, 20 Oct 2026 15:10:32 +0000</pubDate>
      <link>https://dev.to/godnick/how-mac-apps-add-text-suggestions-to-every-app-the-accessibility-api-explained-1bfj</link>
      <guid>https://dev.to/godnick/how-mac-apps-add-text-suggestions-to-every-app-the-accessibility-api-explained-1bfj</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I build DraftKey, an on-device writing assistant for Mac that's built on the APIs below. It comes up once at the end. This post is about the platform, not the product.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Grammarly, autocomplete tools, text expanders, window managers and screen readers all have the same problem on macOS. They need to work inside apps they didn't write. Nearly all of them solve it with the &lt;strong&gt;Accessibility API&lt;/strong&gt;, the same interface VoiceOver uses. If you've ever wondered why one of these tools works in Mail but not in some editor, or why it asks for a scary-sounding permission, this is how the pieces fit together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: become a trusted client
&lt;/h2&gt;

&lt;p&gt;Your app can't read other apps' UI until the user adds it under &lt;strong&gt;System Settings → Privacy &amp;amp; Security → Accessibility&lt;/strong&gt;. You check for that and ask for it with one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;ApplicationServices&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;kAXTrustedCheckOptionPrompt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeUnretainedValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFDictionary&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;trusted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AXIsProcessTrustedWithOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// shows the system prompt if needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AXIsProcessTrustedWithOptions&lt;/code&gt; returns whether your process is a trusted accessibility client. With the prompt option set, macOS shows its own dialog that points the user to System Settings. You can't grant this permission in code, and that's on purpose.&lt;/p&gt;

&lt;p&gt;The permission is broad. A trusted client can read the contents and structure of almost any app's windows. That's why it's worth knowing who made any tool you grant it to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: find the focused text field
&lt;/h2&gt;

&lt;p&gt;Everything in the Accessibility API is an &lt;code&gt;AXUIElement&lt;/code&gt;, an opaque reference to a UI object in another process, with named attributes you can read. Start at the system-wide element and ask what has focus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;focusedElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;AXUIElement&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AXUIElementCreateSystemWide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CFTypeRef&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="kt"&gt;AXUIElementCopyAttributeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kAXFocusedUIElementAttribute&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;AXUIElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, these are the attributes a writing tool cares about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kAXRoleAttribute&lt;/code&gt; / &lt;code&gt;kAXSubroleAttribute&lt;/code&gt;: is this a text field, a text area, or something else?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kAXValueAttribute&lt;/code&gt;: the full text of the field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kAXSelectedTextRangeAttribute&lt;/code&gt;: the selection as a character range (not bytes). An empty range gives you the caret position. Apple's docs say this attribute is required for all editable text elements, which is why it's the most reliable one to build on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kAXSelectedTextAttribute&lt;/code&gt;: just the selected text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the value and the caret position, you have the context a model needs: the text before the cursor, and maybe a little after it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: find where the caret is on screen
&lt;/h2&gt;

&lt;p&gt;To draw ghost text or a popup next to the cursor, you need screen coordinates. That's what &lt;strong&gt;parameterized attributes&lt;/strong&gt; are for. &lt;code&gt;kAXBoundsForRangeParameterizedAttribute&lt;/code&gt; takes a range and returns the bounding rectangle of that text as it appears on screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;caretRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AXUIElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CFRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;axRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AXValueCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfRange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CFTypeRef&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="kt"&gt;AXUIElementCopyParameterizedAttributeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kAXBoundsForRangeParameterizedAttribute&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axRange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGRect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;
    &lt;span class="kt"&gt;AXValueGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;AXValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cgRect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two practical notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some apps return an empty rectangle for a zero-length range. A common fallback is to ask for the bounds of the character just before the caret and use its right edge.&lt;/li&gt;
&lt;li&gt;The rectangle uses top-left-origin screen coordinates, while AppKit windows use bottom-left. Flip the y value against the main screen's height before you position your overlay.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: react to typing without polling
&lt;/h2&gt;

&lt;p&gt;You don't want to read the whole field 60 times a second. &lt;code&gt;AXObserver&lt;/code&gt; lets you subscribe to notifications from another process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AXObserver&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="kt"&gt;AXObserverCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="c1"&gt;// debounce, then re-read value + selection and request a suggestion&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AXUIElementCreateApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;AXObserverAddNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kAXFocusedUIElementChangedNotification&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;AXObserverAddNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kAXValueChangedNotification&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;AXObserverAddNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kAXSelectedTextChangedNotification&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kt"&gt;CFRunLoopAddSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CFRunLoopGetCurrent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                   &lt;span class="kt"&gt;AXObserverGetRunLoopSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need one observer per app process, so watch &lt;code&gt;NSWorkspace&lt;/code&gt; for app activation and move your observer as the user switches apps. Value-changed tells you the user typed. Selected-text-changed tells you the caret moved, which is often your cue to throw away a stale suggestion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: put the text back
&lt;/h2&gt;

&lt;p&gt;Once the user accepts a suggestion, there are two common ways to insert it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set the selected text.&lt;/strong&gt; Writing to &lt;code&gt;kAXSelectedTextAttribute&lt;/code&gt; with &lt;code&gt;AXUIElementSetAttributeValue&lt;/code&gt; replaces the current selection, or inserts at the caret if nothing is selected. It's clean when it works, and it leaves the clipboard alone. Not every app supports writing it, so check the result code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paste it.&lt;/strong&gt; Put the text on the pasteboard and post a synthesized ⌘V with &lt;code&gt;CGEvent&lt;/code&gt;. It works in more places, but you have to save and restore the user's clipboard, and some apps handle pasted text differently from typed text.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many tools try the first and fall back to the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it doesn't work everywhere
&lt;/h2&gt;

&lt;p&gt;This is the part users actually notice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apps expose different amounts.&lt;/strong&gt; Native AppKit text views implement everything above. Custom-drawn editors (some code editors, canvas-based web apps, games) may expose little or nothing. Your tool can only see what the app reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Electron and Chromium build their tree on demand.&lt;/strong&gt; Per &lt;a href="https://www.electronjs.org/docs/latest/tutorial/accessibility" rel="noopener noreferrer"&gt;Electron's accessibility docs&lt;/a&gt;, Electron apps turn on accessibility support automatically when assistive technology like VoiceOver is running. A third-party tool can request it by setting the &lt;code&gt;AXManualAccessibility&lt;/code&gt; attribute to true on the app's element. Until then, the text you're looking for may not be in the tree at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secure fields are off-limits, and should be.&lt;/strong&gt; Password fields have the subrole &lt;code&gt;kAXSecureTextFieldSubrole&lt;/code&gt;, which Apple describes as a field for sensitive data that shows input as bullets. Skip them. Separately, when an app turns on Secure Event Input, which &lt;code&gt;NSSecureTextField&lt;/code&gt; does automatically and Terminal and iTerm let you keep on, Apple's &lt;a href="https://developer.apple.com/library/archive/technotes/tn2150/_index.html" rel="noopener noreferrer"&gt;TN2150&lt;/a&gt; explains that keyboard event taps stop receiving keystrokes. You can check this with &lt;code&gt;IsSecureEventInputEnabled()&lt;/code&gt; and show the user why suggestions paused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mac App Store is mostly out.&lt;/strong&gt; Apple's App Sandbox documentation lists "use of accessibility APIs in assistive apps" among the things the sandbox forbids, and Mac App Store apps must be sandboxed. That's why most system-wide writing tools are downloads signed with Developer ID, not App Store apps. (Event taps that need only Input Monitoring are a narrower exception.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility Inspector&lt;/strong&gt; (Xcode → Open Developer Tool) shows exactly which attributes an app exposes. Check it before you blame your code.&lt;/li&gt;
&lt;li&gt;During development, macOS ties the permission to your code signature, so a rebuilt binary with a different signature can quietly lose trust. &lt;code&gt;tccutil reset Accessibility &amp;lt;bundle-id&amp;gt;&lt;/code&gt; gives you a clean slate.&lt;/li&gt;
&lt;li&gt;Every AX call is a cross-process message to the target app. Keep them off the main thread, and set a timeout with &lt;code&gt;AXUIElementSetMessagingTimeout&lt;/code&gt; so a frozen app can't freeze yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the model fits
&lt;/h2&gt;

&lt;p&gt;All of the above is plumbing. The model only sees a string of context and returns a string. That separation is what makes on-device suggestions practical. The Accessibility layer collects the text locally, and if inference also runs locally, the text never has to leave the Mac.&lt;/p&gt;

&lt;p&gt;That's the design I went with for &lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;DraftKey&lt;/a&gt;. It uses Accessibility to work in compatible text fields, skips secure fields, has per-app controls, and runs its writing model on the Mac. It hits the same limits described above, so it can't work in editors that don't expose their text.&lt;/p&gt;

&lt;p&gt;If you're building something in this space, start with Accessibility Inspector and a single &lt;code&gt;AXObserver&lt;/code&gt;. You'll learn most of what you need from watching which notifications actually fire.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>swift</category>
      <category>a11y</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to make Claude Code's usage limits last longer: 9 practical habits</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Mon, 19 Oct 2026 22:50:40 +0000</pubDate>
      <link>https://dev.to/godnick/how-to-make-claude-codes-usage-limits-last-longer-9-practical-habits-10fb</link>
      <guid>https://dev.to/godnick/how-to-make-claude-codes-usage-limits-last-longer-9-practical-habits-10fb</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I build TokenBar, a Mac menu bar app that tracks AI usage. It comes up once near the end. Everything else here works without it, and most of it comes straight from Anthropic's own &lt;a href="https://code.claude.com/docs/en/costs" rel="noopener noreferrer"&gt;cost docs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you're on a Pro or Max plan, the limit you hit isn't a dollar amount. It's a &lt;strong&gt;session window that resets every five hours&lt;/strong&gt; and a &lt;strong&gt;weekly limit&lt;/strong&gt;, and they're shared with everything else you do in Claude, including chats on claude.ai. When you run out halfway through a refactor, it's usually not because you asked for too much. It's because each request carried far more context than it needed.&lt;/p&gt;

&lt;p&gt;Here's the key fact: &lt;strong&gt;Claude Code sends your whole conversation with every request&lt;/strong&gt;, and every tool call sends another request carrying those results. Prompt caching makes re-reading that history cheaper, but it isn't free. A one-line question in a session that's been open all day still draws usage for the entire conversation.&lt;/p&gt;

&lt;p&gt;Almost every habit below comes back to that.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;/clear&lt;/code&gt; between unrelated tasks
&lt;/h2&gt;

&lt;p&gt;This is the biggest win and the one people skip. If you've finished fixing a bug and now want to write docs, the old bug context is dead weight on every later message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/rename auth-bugfix   # so you can find it later
/clear                # start fresh, costs nothing
/resume               # come back to the old one if you need it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The docs say it plainly: stale context wastes tokens on every message that follows, and &lt;code&gt;/clear&lt;/code&gt; costs nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Compact on purpose, and tell it what to keep
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/compact&lt;/code&gt; summarizes the conversation so far to free up space, but &lt;strong&gt;compacting is itself a large request&lt;/strong&gt;, because it has to read everything it's summarizing. So don't compact out of habit. Do it when you want to keep going on the same task, and give it instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/compact Keep the failing test names, the final schema, and the files we changed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set default instructions in &lt;code&gt;CLAUDE.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Compact instructions&lt;/span&gt;

When you are using compact, please focus on test output and code changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't need continuity, &lt;code&gt;/clear&lt;/code&gt; is cheaper than &lt;code&gt;/compact&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Mind the cache after a break
&lt;/h2&gt;

&lt;p&gt;Prompt caching is why long sessions are affordable at all. On a subscription, the cache lasts &lt;strong&gt;an hour&lt;/strong&gt; (it drops to five minutes once you're drawing on usage credits). Your first message after a longer break misses the cache and &lt;strong&gt;reprocesses your full context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, if you're coming back to a huge session after lunch, it's often cheaper to &lt;code&gt;/clear&lt;/code&gt; and restate the task in a few lines than to type "ok, continue" into a cold 200k-token conversation. &lt;code&gt;/usage&lt;/code&gt; now shows a prompt cache line with your hit rate and whether the cache is warm, so you can check before you decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Match the model to the job
&lt;/h2&gt;

&lt;p&gt;Anthropic's own advice: Sonnet handles most coding tasks well and costs less than Opus, so save Opus for complex architecture decisions and multi-step reasoning. Switch mid-session with &lt;code&gt;/model&lt;/code&gt;, or set a default in &lt;code&gt;/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two details worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching to Opus also applies to subagents that inherit your session's model. For simple subagent work, set &lt;code&gt;model: haiku&lt;/code&gt; in the subagent config.&lt;/li&gt;
&lt;li&gt;If you hit a model-specific limit ("You've hit your Opus limit"), switching to another model family keeps you working. If you hit your &lt;strong&gt;session&lt;/strong&gt; or &lt;strong&gt;weekly&lt;/strong&gt; limit, switching models won't help, because those are shared across models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Turn thinking down for easy tasks
&lt;/h2&gt;

&lt;p&gt;Extended thinking is on by default because it helps with planning and hard reasoning, but &lt;strong&gt;thinking tokens are billed as output tokens&lt;/strong&gt;, and the default budget can be tens of thousands of tokens per request. Renaming a variable doesn't need that.&lt;/p&gt;

&lt;p&gt;Lower it with &lt;code&gt;/effort&lt;/code&gt; (or from &lt;code&gt;/model&lt;/code&gt;) for routine work. Some models always use extended thinking, so effort level is the control that works everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Shrink what loads every session
&lt;/h2&gt;

&lt;p&gt;Some context gets loaded before you've typed anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is loaded at session start.&lt;/strong&gt; The docs suggest keeping it under about 200 lines. Move detailed workflow instructions (PR reviews, migrations, release steps) into &lt;strong&gt;skills&lt;/strong&gt;, which only load when they're used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers add overhead.&lt;/strong&gt; Tool definitions are deferred by default, but server names and instructions still go in. Run &lt;code&gt;/mcp&lt;/code&gt; and disable the servers you're not using. When there's a CLI for something (&lt;code&gt;gh&lt;/code&gt;, &lt;code&gt;aws&lt;/code&gt;, &lt;code&gt;gcloud&lt;/code&gt;, &lt;code&gt;sentry-cli&lt;/code&gt;), it's more context-efficient than an MCP server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run &lt;code&gt;/context&lt;/code&gt; to see what's actually filling your window. It's often a surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Be specific, and plan before you build
&lt;/h2&gt;

&lt;p&gt;"Improve this codebase" makes Claude scan broadly. "Add input validation to the login function in auth.ts" lets it read a couple of files and get to work.&lt;/p&gt;

&lt;p&gt;For bigger changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Press &lt;strong&gt;Shift+Tab&lt;/strong&gt; to switch to &lt;strong&gt;plan mode&lt;/strong&gt; first. Claude explores and proposes an approach, so you don't pay for an implementation going the wrong way.&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Escape&lt;/strong&gt; as soon as it heads off course, and use &lt;code&gt;/rewind&lt;/code&gt; (or double-tap Escape) to go back to a checkpoint.&lt;/li&gt;
&lt;li&gt;Give it a way to check itself: a test to pass, an expected output, a screenshot. Fewer "that's still broken" round trips means fewer tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Keep noisy output out of the main conversation
&lt;/h2&gt;

&lt;p&gt;A 10,000-line test log in your context gets re-sent with every later message. Two ways to avoid that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooks.&lt;/strong&gt; A &lt;code&gt;PreToolUse&lt;/code&gt; hook can rewrite test commands to return only the failures, cutting tens of thousands of tokens down to hundreds. The cost docs include a ready-made &lt;code&gt;filter-test-output.sh&lt;/code&gt; example for &lt;code&gt;npm test&lt;/code&gt;, &lt;code&gt;pytest&lt;/code&gt; and &lt;code&gt;go test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagents.&lt;/strong&gt; Hand verbose work (running tests, reading docs, digging through logs) to a subagent so only a summary comes back. Subagent requests still count toward your usage, so pair this with a smaller model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Watch what runs when you're not typing
&lt;/h2&gt;

&lt;p&gt;Some usage happens in the background:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled tasks and &lt;code&gt;/loop&lt;/code&gt;&lt;/strong&gt; fire on their interval even when the session is idle, sending your full context each time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent teams&lt;/strong&gt; use about &lt;strong&gt;7x&lt;/strong&gt; more tokens than a normal session when teammates run in plan mode, because each one has its own context. Keep teams small and shut teammates down when they're done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-idle sessions&lt;/strong&gt; can wake up for cross-session messages or goal check-ins, and each of those is a full-context turn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, once a week, open &lt;code&gt;/usage&lt;/code&gt; and look at the breakdown. On paid plans it shows how your recent usage splits across skills, subagents, plugins and MCP servers, lists your heaviest loops, and flags any behavior (like long context or cache misses) that's 10% or more of your usage, with a tip to fix it. Press &lt;code&gt;d&lt;/code&gt; or &lt;code&gt;w&lt;/code&gt; to switch between the last day and the last week. It only counts this machine, so claude.ai chats and other computers won't show up there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The common thread: notice before you hit the wall
&lt;/h2&gt;

&lt;p&gt;Every habit above is easier when you can see your usage climbing &lt;em&gt;while&lt;/em&gt; you work, not after you get cut off. &lt;code&gt;/usage&lt;/code&gt; is free and official. I got tired of checking it, so I built &lt;a href="https://tokenbar.site" rel="noopener noreferrer"&gt;TokenBar&lt;/a&gt;, which keeps Claude, Codex and other providers' usage and cost in the Mac menu bar. A free tracker or a status line works too. What matters is having the number in view.&lt;/p&gt;

&lt;p&gt;If I had to pick just three habits: &lt;strong&gt;&lt;code&gt;/clear&lt;/code&gt; between tasks, Sonnet by default, and a short &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/strong&gt; Those three cover most of the waste I've seen.&lt;/p&gt;

&lt;p&gt;What's the habit that saved you the most? I'm collecting tips for a follow-up.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Private AI writing assistants for Mac in 2026: what actually runs on-device</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Mon, 19 Oct 2026 20:39:32 +0000</pubDate>
      <link>https://dev.to/godnick/private-ai-writing-assistants-for-mac-in-2026-what-actually-runs-on-device-mcg</link>
      <guid>https://dev.to/godnick/private-ai-writing-assistants-for-mac-in-2026-what-actually-runs-on-device-mcg</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I build DraftKey, one of the tools below. I've tried to describe every option, mine included, as plainly as I can.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Lots of Mac writing tools now call themselves "private," and the word covers very different things. Some send every keystroke to a server under a privacy policy. Some run a model on your Mac but still call out for harder requests. Some never send your text anywhere.&lt;/p&gt;

&lt;p&gt;If you write client emails, legal drafts, health notes or code comments you don't want stored on someone else's server, that difference matters. Here's how the main options compare in October 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "on-device" should mean
&lt;/h2&gt;

&lt;p&gt;When I evaluate a tool, I ask four questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Where does inference run?&lt;/strong&gt; On your Mac's GPU or Neural Engine, or on a server?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there a cloud fallback?&lt;/strong&gt; Some tools quietly hand harder requests to a remote model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What still needs the internet?&lt;/strong&gt; Model downloads, license checks and updates are normal. Sending your text is a different thing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can you turn it off per app?&lt;/strong&gt; You probably don't want any assistant reading your password manager or your terminal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Almost every system-wide Mac writing tool works through &lt;strong&gt;macOS Accessibility&lt;/strong&gt;, which lets it read the text field you're typing in. That's how they work across apps, and it's also why you should trust whoever made the one you install.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cloud option: Grammarly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.grammarly.com/desktop/mac" rel="noopener noreferrer"&gt;Grammarly&lt;/a&gt; is still the default for many people. The Mac app works across desktop apps and browsers, and its grammar and tone suggestions are mature.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;cloud-based&lt;/strong&gt;: text is sent to Grammarly's servers for analysis, and it needs an internet connection. You can turn it off per app, and it tries to skip sensitive fields such as passwords. There's a free plan, and Grammarly Pro is listed at $12/month billed annually or $30 month-to-month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; polished grammar and tone suggestions when cloud processing is acceptable.&lt;br&gt;
&lt;strong&gt;Not for:&lt;/strong&gt; anything you're not allowed to send to a third party.&lt;/p&gt;

&lt;h2&gt;
  
  
  The built-in option: Apple Intelligence Writing Tools
&lt;/h2&gt;

&lt;p&gt;On Apple silicon Macs with Apple Intelligence turned on, &lt;strong&gt;Writing Tools&lt;/strong&gt; can proofread, rewrite, change tone and summarize text in most apps, for free.&lt;/p&gt;

&lt;p&gt;Apple's privacy page says basic requests run on-device, but &lt;strong&gt;more complex requests can go to Private Cloud Compute&lt;/strong&gt;. That's Apple's server setup, which Apple says only processes the data needed for the request, doesn't store it, and can't be accessed by Apple. You can review those requests in System Settings â†’ Privacy &amp;amp; Security â†’ Apple Intelligence Report. macOS also has its own inline predictive text for short completions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; free, good-enough rewrites with a strong privacy design.&lt;br&gt;
&lt;strong&gt;Caveat:&lt;/strong&gt; "mostly on-device" isn't the same as "always on-device." It's a request-based tool, so you select text and pick an action. It's not continuous AI autocomplete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Third-party apps that run on-device
&lt;/h2&gt;

&lt;p&gt;All of these need an Apple Silicon Mac, and most need macOS 14 or later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://cotypist.app" rel="noopener noreferrer"&gt;Cotypist&lt;/a&gt;&lt;/strong&gt; is AI autocomplete that predicts your next words in any Mac app, running on your Mac. It offers the most personalization of any app here: learning your voice, per-app instructions, and screen- and clipboard-aware suggestions on higher tiers. There's a free tier (100 accepted completions a day) and a 30-day Pro trial. After that, Plus is $72/year for one Mac and Pro is $108/year for up to three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://shadowtype.app" rel="noopener noreferrer"&gt;Shadowtype&lt;/a&gt;&lt;/strong&gt; is free and open source. It runs GGUF models through llama.cpp and Metal, showing next-word suggestions in light gray text (ghost text) plus rewrites of selected text with âŒ¥âŒ˜K: improve, shorten, make formal or casual, fix grammar, summarize. You can bring your own model, and it runs a local OpenAI-compatible endpoint and an MCP bridge, which developers will like. The trade-off is that you manage more yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://refine.sh" rel="noopener noreferrer"&gt;Refine&lt;/a&gt;&lt;/strong&gt; is an offline grammar checker built on a local Gemma model. It checks as you type, explains each fix, and supports many languages, with a floating editor for rewrites and translation. Its site lists it as free with no account. Note that it &lt;strong&gt;optionally&lt;/strong&gt; lets you plug in your own API key for cloud models. That's off by default, but if you enable it, those requests leave your Mac.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://typetab.app" rel="noopener noreferrer"&gt;TypeTab&lt;/a&gt;&lt;/strong&gt; is focused on autocomplete. Predictions run on the Apple Neural Engine, and it says no account and no telemetry. It's free to try, then $49 one-time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;DraftKey&lt;/a&gt;&lt;/strong&gt; is my app. It does on-device autocomplete plus instruction-based rewriting: tap âŒ˜ and âŒ¥ together in a text field, choose an action or type a request like "make it shorter," review the result, then insert it. It works on the current paragraph or your selection. It also has optional mechanical keyboard sounds and a local clipboard history, each with its own toggle. The writing model is a separate ~1.12 GB download, and AI features need 8 GB of memory. After setup the model works offline. The internet is only used for the model download, license activation and update checks. There are per-app controls, and secure fields are excluded. It's a $5 lifetime license for up to three Macs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weak spots:&lt;/strong&gt; it's a young app, the model is small so it won't match a large cloud model on complex rewrites, and some custom editors don't expose their text to Accessibility, so it can't work there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Where text is processed&lt;/th&gt;
&lt;th&gt;Autocomplete&lt;/th&gt;
&lt;th&gt;Rewriting&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Requirements&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grammarly&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Suggestions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free plan; Pro $12/mo annual or $30/mo&lt;/td&gt;
&lt;td&gt;Internet required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple Writing Tools&lt;/td&gt;
&lt;td&gt;On-device, complex requests may use Private Cloud Compute&lt;/td&gt;
&lt;td&gt;No (macOS has basic predictive text)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Apple silicon, Apple Intelligence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cotypist&lt;/td&gt;
&lt;td&gt;On-device&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Word alternatives&lt;/td&gt;
&lt;td&gt;Free tier; $72â€“$108/yr&lt;/td&gt;
&lt;td&gt;macOS 14+, Apple Silicon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shadowtype&lt;/td&gt;
&lt;td&gt;On-device&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free, open source&lt;/td&gt;
&lt;td&gt;macOS 14+, Apple Silicon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refine&lt;/td&gt;
&lt;td&gt;On-device (optional bring-your-own-key cloud)&lt;/td&gt;
&lt;td&gt;No (grammar checks)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;macOS 14+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeTab&lt;/td&gt;
&lt;td&gt;On-device&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;$49 one-time&lt;/td&gt;
&lt;td&gt;macOS 14+, Apple Silicon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DraftKey&lt;/td&gt;
&lt;td&gt;On-device&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;$5 lifetime (up to 3 Macs)&lt;/td&gt;
&lt;td&gt;macOS 14+, Apple Silicon, 8 GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How I'd choose
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your text may never leave the machine&lt;/strong&gt; (client work, NDAs, regulated data): rule out Grammarly, and remember that Apple's Writing Tools can use Private Cloud Compute. Pick one of the on-device apps and check its network activity yourself with Little Snitch or LuLu.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You mainly want grammar fixes:&lt;/strong&gt; Refine, or Apple's Writing Tools if PCC is fine for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You mainly want autocomplete:&lt;/strong&gt; Cotypist if you want deep personalization and don't mind a subscription, TypeTab for a one-time purchase, Shadowtype for free and open source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want autocomplete and rewriting in one small paid app:&lt;/strong&gt; DraftKey or Shadowtype. Shadowtype is free and more hackable. DraftKey is more of a ready-to-use app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A note on testing claims
&lt;/h2&gt;

&lt;p&gt;Don't take "100% private" on trust, mine included. Install the tool, turn on a network monitor, type a paragraph, and watch what connects. An on-device assistant should be quiet while you write, apart from the license and update checks it tells you about. Ten minutes of testing tells you more than any marketing page.&lt;/p&gt;

&lt;p&gt;Which one are you using, and did it pass the network-monitor test?&lt;/p&gt;

</description>
      <category>macos</category>
      <category>ai</category>
      <category>privacy</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to track Claude Code and Codex usage limits on a Mac (5 ways compared)</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Mon, 19 Oct 2026 20:39:25 +0000</pubDate>
      <link>https://dev.to/godnick/how-to-track-claude-code-and-codex-usage-limits-on-a-mac-5-ways-compared-1641</link>
      <guid>https://dev.to/godnick/how-to-track-claude-code-and-codex-usage-limits-on-a-mac-5-ways-compared-1641</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I build TokenBar, one of the tools below. I've tried to keep this useful whichever one you pick, and most of the options here are free.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you use Claude Code or Codex on a subscription, you've probably hit a limit halfway through a task. Neither tool shows your remaining usage by default, so it's easy to burn through a session window without noticing.&lt;/p&gt;

&lt;p&gt;This guide covers five ways to keep an eye on it on a Mac, starting with the free, built-in ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, how the limits actually work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude (Pro, Max, Team).&lt;/strong&gt; Paid plans have two kinds of limits: a &lt;strong&gt;session limit that resets every five hours&lt;/strong&gt; and &lt;strong&gt;weekly limits&lt;/strong&gt; that reset once a week. Max plans also have a separate weekly limit for some models. Usage from claude.ai, Claude Desktop and Claude Code all counts toward the &lt;strong&gt;same&lt;/strong&gt; pool, so a long chat in the browser eats into your coding budget too. How fast you use it depends on the model, context length, effort level and the features you use. (&lt;a href="https://support.claude.com/en/articles/11647753-how-do-usage-and-length-limits-work" rel="noopener noreferrer"&gt;Claude Help Center&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex (ChatGPT plans).&lt;/strong&gt; Codex comes with ChatGPT plans, and its usage depends on the model, task complexity, context, reasoning and tools. Plans have had a five-hour window plus weekly limits, but OpenAI has changed this more than once in 2026. The five-hour window was temporarily lifted for some plans, and Pro currently has no five-hour limit. Codex also shares its allowance with other agent features like ChatGPT Work. Check your own account rather than trusting a blog post, this one included. (&lt;a href="https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan-usage-limits-and-troubleshooting" rel="noopener noreferrer"&gt;OpenAI Help Center&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Built-in commands (free, zero setup)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude Code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/usage&lt;/code&gt; shows session cost, plan usage limits (the five-hour session and weekly bars with reset times) and activity stats. &lt;code&gt;/cost&lt;/code&gt; and &lt;code&gt;/stats&lt;/code&gt; are aliases for it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/context&lt;/code&gt; shows what's filling your context window as a colored grid.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/compact [instructions]&lt;/code&gt; summarizes the conversation so far to free up context. A bloated context makes every later turn cost more, so compacting early is one of the easiest ways to make your limit last.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/usage-credits&lt;/code&gt; opens usage-credit settings when you hit a limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Codex CLI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/status&lt;/code&gt; in an active session shows your remaining limits and reset times.&lt;/li&gt;
&lt;li&gt;The Codex usage dashboard (Settings â†’ Usage) is the official source of truth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; official numbers, nothing to install, nothing to trust.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; you have to remember to check, it only works inside a session, and you see one tool at a time.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. ccusage (free, open source CLI)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ccusage/ccusage" rel="noopener noreferrer"&gt;ccusage&lt;/a&gt; reads the usage data that coding agent CLIs store locally and turns it into daily, weekly, monthly and per-session reports with token counts and estimated costs. It now covers far more than Claude Code: Codex, Gemini CLI, GitHub Copilot CLI, OpenCode, Amp and many others.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ccusage@latest          &lt;span class="c"&gt;# all detected sources, by day&lt;/span&gt;
npx ccusage claude daily    &lt;span class="c"&gt;# just Claude Code&lt;/span&gt;
npx ccusage codex daily     &lt;span class="c"&gt;# just Codex&lt;/span&gt;
npx ccusage blocks          &lt;span class="c"&gt;# Claude Code 5-hour windows&lt;/span&gt;
npx ccusage statusline      &lt;span class="c"&gt;# status line for Claude Code hooks (beta)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ccusage blocks&lt;/code&gt; is the most useful one for limits, because it groups your Claude Code activity into five-hour windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; free, scriptable, &lt;code&gt;--json&lt;/code&gt; output, works offline from local files, covers lots of tools.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; it's an estimate built from your local logs. It can't see your plan's actual remaining percentage, and it doesn't count usage from claude.ai or other machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Read the logs yourself (free, maximum control)
&lt;/h2&gt;

&lt;p&gt;If you want your own dashboard or alerts, the raw data is on disk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code:&lt;/strong&gt; &lt;code&gt;~/.claude/projects/&amp;lt;encoded-project-path&amp;gt;/&amp;lt;session-id&amp;gt;.jsonl&lt;/code&gt;, plus &lt;code&gt;subagents/&lt;/code&gt; folders in newer versions. Assistant records carry a &lt;code&gt;message.usage&lt;/code&gt; object with input, output and cache tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex CLI:&lt;/strong&gt; &lt;code&gt;~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl&lt;/code&gt;. Look for &lt;code&gt;event_msg&lt;/code&gt; records whose payload type is &lt;code&gt;token_count&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two traps people hit when summing these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code duplicates.&lt;/strong&gt; Resumed sessions can repeat earlier assistant turns, so deduplicate on &lt;code&gt;message.id&lt;/code&gt; (plus &lt;code&gt;requestId&lt;/code&gt;) or your totals come out inflated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex cumulative totals.&lt;/strong&gt; &lt;code&gt;token_count&lt;/code&gt; events carry both &lt;code&gt;total_token_usage&lt;/code&gt; (a running total) and &lt;code&gt;last_token_usage&lt;/code&gt; (just that turn). Sum &lt;code&gt;last_token_usage&lt;/code&gt; only. Also note that OpenAI's &lt;code&gt;input_tokens&lt;/code&gt; already includes &lt;code&gt;cached_input_tokens&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither format is officially documented, so expect it to change between versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; total control, no third-party code.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; real work to build, breaks when formats change, and like ccusage it can't see your server-side remaining quota.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Free menu bar apps
&lt;/h2&gt;

&lt;p&gt;A menu bar app keeps the numbers visible without you having to ask. These three are free and open source (MIT):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/steipete/CodexBar" rel="noopener noreferrer"&gt;CodexBar&lt;/a&gt;&lt;/strong&gt; (macOS 14+, &lt;code&gt;brew install --cask codexbar&lt;/code&gt;). It shows limits and reset countdowns for a long list of providers: Codex, Claude, Cursor, Gemini, Copilot and dozens more. You get one menu bar item per provider, or a merged icon. For Claude it uses OAuth, browser cookies or the CLI, and it ships a &lt;code&gt;codexbar&lt;/code&gt; CLI for scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/hamed-elfayome/Claude-Usage-Tracker" rel="noopener noreferrer"&gt;Claude Usage Tracker&lt;/a&gt;&lt;/strong&gt; (macOS 14+, installed with Homebrew). It tracks Claude's five-hour session, weekly and per-model usage, with optional Codex tracking. Its standout feature is support for multiple Claude accounts with their own credentials, plus status line integration, usage history charts and threshold notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/f-is-h/Usage4Claude" rel="noopener noreferrer"&gt;Usage4Claude&lt;/a&gt;&lt;/strong&gt; (macOS 13+, installed from a DMG, Intel or Apple silicon). It tracks Claude's five-hour, seven-day, extra-usage and per-model limits, plus Codex's five-hour, seven-day and credit balance. Its "pace" graph shows whether you're using your allowance faster than time is passing, which is handy for weekly limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; they're free and always visible, and they show your real remaining quota rather than an estimate from logs.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; most read credentials from your Keychain or browser cookies, so read what each one accesses. Some depend on unofficial endpoints that can break when a provider changes things.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. TokenBar (paid)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tokenbar.site" rel="noopener noreferrer"&gt;TokenBar&lt;/a&gt; is my app, so weigh this section accordingly. It's a macOS 14+ menu bar app that brings Claude, Codex, Cursor, Gemini and Copilot into one place: allowances, credits and reset timing, plus a spending view with cost history. It also adds agent notifications for finished tasks, input requests, approvals and stalls on supported integrations, and "Pixel Agents," a small pixel-art office that shows which coding sessions are working or need attention. It also shows CPU, memory and energy so you can see what your agents are doing to your Mac.&lt;/p&gt;

&lt;p&gt;Pricing: Pro is &lt;strong&gt;$15 once&lt;/strong&gt; for a lifetime license on one Mac, or Basic is $5/month. You buy inside the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; one view across providers, agent notifications, a polished native UI.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; it costs money while the apps above are free. Coverage varies by integration, and it's a one-Mac license.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Shows real remaining quota&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;th&gt;Codex&lt;/th&gt;
&lt;th&gt;Always visible&lt;/th&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/usage&lt;/code&gt;, &lt;code&gt;/status&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ccusage&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;No (estimate from logs)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Via status line&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw JSONL logs&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;If you build it&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodexBar&lt;/td&gt;
&lt;td&gt;Free (MIT)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Usage Tracker&lt;/td&gt;
&lt;td&gt;Free (MIT)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage4Claude&lt;/td&gt;
&lt;td&gt;Free (MIT)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TokenBar&lt;/td&gt;
&lt;td&gt;$15 lifetime / $5 mo&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Which should you use?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You check occasionally:&lt;/strong&gt; &lt;code&gt;/usage&lt;/code&gt; and &lt;code&gt;/status&lt;/code&gt; are enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want history and cost reports:&lt;/strong&gt; ccusage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want it always visible, for free:&lt;/strong&gt; CodexBar if you use many providers, Claude Usage Tracker if you juggle several Claude accounts, Usage4Claude if you like pace tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want spending, agent notifications and multiple providers in one paid app:&lt;/strong&gt; TokenBar is one option.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whatever you pick, the habits that stretch a limit the most are the same: &lt;code&gt;/compact&lt;/code&gt; before context gets huge, use smaller models for routine work, and keep in mind that your claude.ai chats share the same pool as Claude Code.&lt;/p&gt;

&lt;p&gt;What are you using to stay under your limits? I'd like to hear about setups I've missed.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>productivity</category>
      <category>macos</category>
    </item>
    <item>
      <title>Shipping on-device AI on the Mac: the requirements and tradeoffs I didn't want to hide</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Tue, 13 Oct 2026 17:18:29 +0000</pubDate>
      <link>https://dev.to/godnick/shipping-on-device-ai-on-the-mac-the-requirements-and-tradeoffs-i-didnt-want-to-hide-1dcf</link>
      <guid>https://dev.to/godnick/shipping-on-device-ai-on-the-mac-the-requirements-and-tradeoffs-i-didnt-want-to-hide-1dcf</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;DraftKey&lt;/a&gt;, a native Mac app that brings together AI autocomplete, instruction-based rewriting, and mechanical keyboard sounds. The one constraint I wouldn't bend on: the writing model runs on your Mac, and your text isn't sent to a cloud AI service.&lt;/p&gt;

&lt;p&gt;That choice shapes almost everything else. Here's what it looks like in practice, in case you're weighing local inference for a desktop app of your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The hardware is the first gate
&lt;/h2&gt;

&lt;p&gt;When inference runs locally, the user's machine does the work. DraftKey needs an Apple Silicon Mac on macOS 14 or later, and the AI features need at least 8 GB of memory. That rules some people out, and I'd rather say so on the download page than have someone find out after installing.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The model is its own download
&lt;/h2&gt;

&lt;p&gt;The writing model is a separate download of roughly 1.12 GB, and you need at least 2.2 GB of free space plus room for the app. Onboarding has to explain that clearly, because "download the app" and "download the model" are two different moments for the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. "Offline" needs an honest asterisk
&lt;/h2&gt;

&lt;p&gt;Once the model is downloaded, autocomplete and rewriting work offline. But setup still touches the network: the initial model download, license activation, and update checks. "Your text stays on your Mac" is true. "The app never connects to the internet" would not be, so I don't say it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Working inside other apps means Accessibility
&lt;/h2&gt;

&lt;p&gt;DraftKey works in the apps you already write in by using macOS Accessibility to read and write compatible text fields. Support depends on how each app exposes its text, so some custom editors don't work, and secure fields are always excluded. Per-app controls let you choose exactly where DraftKey is active.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Keep the features independent
&lt;/h2&gt;

&lt;p&gt;Autocomplete suggests what to write next while you type. Rewriting changes text you already have, following an instruction like "make it shorter." Keyboard sounds work with no AI at all. Each has its own switch, so you can run only the parts you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shortcuts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tap Command + Option together, then release, to open Writing tools. It uses your current paragraph automatically, or your selection if you've highlighted text. You review the result before choosing Insert.&lt;/li&gt;
&lt;li&gt;Press ⌘⌥V to open clipboard history, pick an earlier text copy, press Return, then ⌘V to paste. That part needs no model and is saved only on your Mac.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A question for you
&lt;/h2&gt;

&lt;p&gt;If you've shipped on-device ML in a desktop app, how did you handle hardware requirements and big model downloads during onboarding? I'd love to hear what worked and what confused people.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>ai</category>
      <category>privacy</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why I chose on-device inference for a writing assistant</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:54:22 +0000</pubDate>
      <link>https://dev.to/godnick/why-i-chose-on-device-inference-for-a-writing-assistant-479j</link>
      <guid>https://dev.to/godnick/why-i-chose-on-device-inference-for-a-writing-assistant-479j</guid>
      <description>&lt;p&gt;When I built DraftKey, the biggest decision was running the model locally instead of calling a cloud API. Here are the tradeoffs.&lt;/p&gt;

&lt;p&gt;The upside: people's text never leaves their Mac, it works offline, and there are no per-request API costs.&lt;/p&gt;

&lt;p&gt;The downside: the app needs a model download of about 1.1 GB, at least 8 GB of RAM, and an Apple Silicon Mac, and a small model has less raw capability than the frontier ones.&lt;/p&gt;

&lt;p&gt;For short rewrites and autocomplete, a small local model turned out to be good enough, and privacy ended up being the feature people notice most. Curious whether others building local-AI apps landed in the same place. getdraftkey.com&lt;/p&gt;

</description>
      <category>ai</category>
      <category>macos</category>
      <category>privacy</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Shipping a local-LLM writing assistant as a native macOS app</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Sun, 11 Oct 2026 01:00:24 +0000</pubDate>
      <link>https://dev.to/godnick/shipping-a-local-llm-writing-assistant-as-a-native-macos-app-3ddf</link>
      <guid>https://dev.to/godnick/shipping-a-local-llm-writing-assistant-as-a-native-macos-app-3ddf</guid>
      <description>&lt;p&gt;A short write-up on building DraftKey. It uses macOS Accessibility to work in text fields across apps, runs an approximately 1.1 GB model on-device so autocomplete and rewrites work offline, and skips secure fields and password-manager copies. Happy to go deeper on the Accessibility quirks or on-device inference if people are interested. getdraftkey.com&lt;/p&gt;

</description>
      <category>macos</category>
      <category>ai</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What I learned building a system-wide AI writing tool on macOS</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Thu, 08 Oct 2026 20:06:42 +0000</pubDate>
      <link>https://dev.to/godnick/what-i-learned-building-a-system-wide-ai-writing-tool-on-macos-81m</link>
      <guid>https://dev.to/godnick/what-i-learned-building-a-system-wide-ai-writing-tool-on-macos-81m</guid>
      <description>&lt;p&gt;I assumed a Mac app that helps you write in any text field would be the easy part. It wasn't. Here's what I learned building DraftKey, an on-device writing assistant for the apps you already use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading text across apps means Accessibility
&lt;/h2&gt;

&lt;p&gt;There's no public API that says "give me the text the user is typing in whatever app is in front." What macOS does have is the Accessibility API, the same layer screen readers like VoiceOver use.&lt;/p&gt;

&lt;p&gt;The basic flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask for Accessibility permission. The user has to approve your app in System Settings under Privacy &amp;amp; Security, and &lt;code&gt;AXIsProcessTrustedWithOptions&lt;/code&gt; is how you check (and prompt).&lt;/li&gt;
&lt;li&gt;Create a system-wide element with &lt;code&gt;AXUIElementCreateSystemWide()&lt;/code&gt; and ask it for &lt;code&gt;kAXFocusedUIElementAttribute&lt;/code&gt; to find the focused text field.&lt;/li&gt;
&lt;li&gt;Read attributes like &lt;code&gt;kAXValueAttribute&lt;/code&gt;, &lt;code&gt;kAXSelectedTextAttribute&lt;/code&gt;, and &lt;code&gt;kAXSelectedTextRangeAttribute&lt;/code&gt; to get the text and where the cursor is.&lt;/li&gt;
&lt;li&gt;Use parameterized attributes like &lt;code&gt;kAXBoundsForRangeParameterizedAttribute&lt;/code&gt; if you want to draw UI near the caret.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With standard Cocoa text views, this works well. The problem is everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every editor exposes its text
&lt;/h2&gt;

&lt;p&gt;Support depends entirely on how each app exposes its text, and some don't expose much at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editors that draw their own text, like many code editors, terminals, and canvas-based web apps, can give you a focused element with no usable value or selection.&lt;/li&gt;
&lt;li&gt;Chromium and Electron apps often build a fuller accessibility tree only once they know assistive tech is around, so what you see can vary.&lt;/li&gt;
&lt;li&gt;Some apps report a value but ignore writes to it, or write it but don't register an undo step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I stopped promising "works everywhere." DraftKey works in compatible text fields, has per-app controls so you choose where it runs, and skips secure fields completely. Password fields show up with a secure text field subrole, and you should never touch them.&lt;/p&gt;

&lt;p&gt;Writing text back is its own decision. Setting the selected text through Accessibility is clean when an app supports it. The common fallback is going through the pasteboard, which means you're messing with the user's clipboard. Whatever you pick, test it app by app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The modifier-only shortcut
&lt;/h2&gt;

&lt;p&gt;The rewrite panel opens when you tap Command and Option together and let go. A modifier-only shortcut sounds simple, but you're really watching modifier flag changes and deciding "was that a tap, or was the user holding Command+Option on the way to some other shortcut?" If any other key goes down in between, it isn't a tap. Get that wrong and the panel pops up mid shortcut, which is the fastest way to get uninstalled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the model locally has real costs
&lt;/h2&gt;

&lt;p&gt;I wanted writing help that doesn't send your drafts to a cloud AI service. That part works: after a one-time model download of about 1.12 GB, autocomplete and rewriting run offline. The internet is only needed for the download, license activation, and update checks.&lt;/p&gt;

&lt;p&gt;But local has tradeoffs you should be upfront about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAM.&lt;/strong&gt; The AI features need at least 8 GB of memory, since the model shares it with everything else that's open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The download.&lt;/strong&gt; A 1.12 GB download on first launch is friction. I made it a separate step with clear messaging instead of hiding it in the app bundle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed.&lt;/strong&gt; Autocomplete has a tight latency budget. If a suggestion shows up after the user already typed the next word, it's noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware.&lt;/strong&gt; Apple Silicon makes this practical. That's why DraftKey requires an Apple Silicon Mac on macOS 14 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The upside: it works on a plane, and your draft emails stay on your Mac.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review before insert, every time
&lt;/h2&gt;

&lt;p&gt;The design decision I'm most sure about: a rewrite never replaces your text directly. You type an instruction like "make it shorter," it works on the current paragraph (or your selection if you highlighted something), you see the result, and you choose whether to insert it.&lt;/p&gt;

&lt;p&gt;Language models get things wrong sometimes. They drop a detail or change a meaning. A review step costs one keystroke, saves you from sending something you didn't write, and keeps your voice yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything is a toggle
&lt;/h2&gt;

&lt;p&gt;Autocomplete, rewrites, local clipboard history (Command+Option+V), and six mechanical keyboard sound profiles all toggle independently. Some people only want the sounds, which work with the AI turned off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you're curious, there's a free typing test with the keyboard sounds on &lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;getdraftkey.com&lt;/a&gt;. No install needed. And if you've built anything on top of the Accessibility API, I'd love to hear which apps gave you the most trouble.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>ai</category>
      <category>llm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Planning an offline writing session on Mac? Download the model before you go.</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Thu, 08 Oct 2026 15:46:56 +0000</pubDate>
      <link>https://dev.to/godnick/planning-an-offline-writing-session-on-mac-download-the-model-before-you-go-1omf</link>
      <guid>https://dev.to/godnick/planning-an-offline-writing-session-on-mac-download-the-model-before-you-go-1omf</guid>
      <description>&lt;p&gt;A train journey or a few hours away from Wi-Fi can be useful time to draft documentation, support replies, or client emails. With a local writing assistant, the preparation happens before you disconnect: the app, its model, and the document all need to be ready.&lt;/p&gt;

&lt;p&gt;Disclosure: I’m behind DraftKey. This article was written and published by an AI assistant using DraftKey’s documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The installer is only the first download
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.getdraftkey.com/" rel="noopener noreferrer"&gt;DraftKey&lt;/a&gt; provides autocomplete and rewriting in compatible Mac text fields. Its writing model runs on your Mac, but it is downloaded separately from the app.&lt;/p&gt;

&lt;p&gt;The documented setup sequence is to install DraftKey in Applications, follow its permission prompts, activate a purchased license, and let the writing model finish downloading. Clicking &lt;strong&gt;Get started&lt;/strong&gt; begins that model download in the background.&lt;/p&gt;

&lt;p&gt;That last step is easy to overlook. Having the installer on your Mac does not mean the AI is ready for an offline session. Check the model’s download status while you still have a reliable connection.&lt;/p&gt;

&lt;p&gt;The current requirements are Apple Silicon, macOS 14 or later, and at least 8 GB of memory for AI. The model is approximately 1.12 GB; allow at least 2.2 GB of free space for it, plus room for the app. A purchased license is required for activation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare the exact text field you plan to use
&lt;/h2&gt;

&lt;p&gt;DraftKey uses macOS Accessibility to work with text in other apps. In its per-app controls, enable the editor where you intend to write, and enable the writing feature you want.&lt;/p&gt;

&lt;p&gt;Compatibility can vary between fields inside the same app. Secure fields are excluded, and some custom editors do not expose the text access DraftKey needs. An app appearing in your Dock is therefore not enough to establish compatibility.&lt;/p&gt;

&lt;p&gt;Before leaving, try a short, non-sensitive paragraph in the actual field you’ll use. Open Writing tools, request a small change, and confirm you can review and apply the result. If nothing appears, check activation, model readiness, feature settings, Accessibility permission, and the per-app setting in that order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring the document as well as the model
&lt;/h2&gt;

&lt;p&gt;An assistant’s local inference does not make a browser-only editor available offline. Open the document in advance and use your editor’s supported offline or local-save workflow.&lt;/p&gt;

&lt;p&gt;The same distinction matters for privacy: where DraftKey processes a rewrite and whether your editor syncs the document are separate questions. Choose both parts of the workflow deliberately.&lt;/p&gt;

&lt;p&gt;For a support reply, that might mean drafting in an available local document, making a wording pass, and sending it when you reconnect. Confirm the destination editor works for you before depending on it during a journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  What still needs a connection?
&lt;/h2&gt;

&lt;p&gt;After setup, DraftKey’s autocomplete and rewrites use the downloaded model locally. Initial app and model downloads, license activation, and update checks use online services. Finish those setup steps ahead of time.&lt;/p&gt;

&lt;p&gt;If that fits your routine, the &lt;a href="https://www.getdraftkey.com/draftkey-setup" rel="noopener noreferrer"&gt;DraftKey setup guide&lt;/a&gt; covers installation and text-field troubleshooting. The &lt;a href="https://www.getdraftkey.com/local-ai-writing-mac" rel="noopener noreferrer"&gt;local AI guide&lt;/a&gt; explains which operations stay on your Mac and which use the internet.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>productivity</category>
      <category>ai</category>
      <category>writing</category>
    </item>
    <item>
      <title>I stopped pasting my drafts into ChatGPT. Here's the offline Mac app I use instead.</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Thu, 08 Oct 2026 00:51:12 +0000</pubDate>
      <link>https://dev.to/godnick/i-stopped-pasting-my-drafts-into-chatgpt-heres-the-offline-mac-app-i-use-instead-5d42</link>
      <guid>https://dev.to/godnick/i-stopped-pasting-my-drafts-into-chatgpt-heres-the-offline-mac-app-i-use-instead-5d42</guid>
      <description>&lt;p&gt;Every time I wanted a sentence to sound better, I'd copy it, switch to a browser tab, paste it into a cloud AI, wait, copy the result, and switch back. That's five steps, and my unreleased work was going to someone else's server.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;DraftKey&lt;/a&gt;&lt;/strong&gt;. It brings AI writing help into the Mac apps you already write in, and the model runs &lt;strong&gt;entirely on your Mac&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2-second workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Write anywhere: email, Notes, Slack, whatever you use.&lt;/li&gt;
&lt;li&gt;Tap &lt;strong&gt;⌘ + ⌥&lt;/strong&gt; and let go.&lt;/li&gt;
&lt;li&gt;Type "make it shorter" or "sound warmer," then review the rewrite and insert it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You don't need to select anything first, because it works on the paragraph you're in. As you type, autocomplete suggests the next words in compatible text fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your words never leave your Mac
&lt;/h2&gt;

&lt;p&gt;After a one-time model download, autocomplete and rewriting work &lt;strong&gt;offline&lt;/strong&gt;. None of your text goes to a cloud AI service. You choose which apps it runs in, and it skips secure fields automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two extras people end up loving
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clipboard history:&lt;/strong&gt; Copied over something important? Press &lt;strong&gt;⌘⌥V&lt;/strong&gt; to search your recent copies and bring one back. It's stored locally and skips password-manager copies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanical keyboard sounds:&lt;/strong&gt; Every keystroke gets that satisfying click, and it works with the AI turned off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it in 15 seconds, no install
&lt;/h2&gt;

&lt;p&gt;The site has a &lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;typing test&lt;/a&gt; with the keyboard sounds built in, so put on headphones and hear it for yourself. Then grab the app if you're on an Apple Silicon Mac with macOS 14 or later.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://getdraftkey.com" rel="noopener noreferrer"&gt;Get DraftKey&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd love your feedback. Which app should I make sure it works perfectly in next?&lt;/p&gt;

</description>
      <category>macos</category>
      <category>ai</category>
      <category>productivity</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Text replacement, autocomplete, or rewrite? A practical Mac writing workflow</title>
      <dc:creator>Henry Godnick</dc:creator>
      <pubDate>Mon, 05 Oct 2026 18:25:00 +0000</pubDate>
      <link>https://dev.to/godnick/text-replacement-autocomplete-or-rewrite-a-practical-mac-writing-workflow-57pl</link>
      <guid>https://dev.to/godnick/text-replacement-autocomplete-or-rewrite-a-practical-mac-writing-workflow-57pl</guid>
      <description>&lt;p&gt;If you write support replies, bug reports or client emails on a Mac, some sentences repeat exactly. Others need different wording every time. Choosing the right kind of writing help starts with that distinction.&lt;/p&gt;

&lt;p&gt;Disclosure: I’m behind DraftKey, a Mac writing assistant. This article was drafted by an AI assistant using public documentation. The examples are illustrative.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Use text replacement when the wording should stay fixed
&lt;/h2&gt;

&lt;p&gt;Think about a sentence you repeatedly type, such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please include the app version and macOS version in your reply.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You already know the wording. A saved shortcut is enough.&lt;/p&gt;

&lt;p&gt;macOS has built-in Text Replacement. Open System Settings, choose Keyboard, then Text Replacements. Add a short trigger and the text it should expand into.&lt;/p&gt;

&lt;p&gt;For example, you could save ;appdetails as the trigger for that sentence.&lt;/p&gt;

&lt;p&gt;Apple notes that text replacement works in many apps, with exceptions. Check it in the editor where you actually write. &lt;a href="https://support.apple.com/en-ke/guide/mac-help/mh35735/mac" rel="noopener noreferrer"&gt;Apple’s setup guide&lt;/a&gt; explains the settings and app-level controls.&lt;/p&gt;

&lt;p&gt;Good candidates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A standard request for reproduction steps&lt;/li&gt;
&lt;li&gt;A frequently used sign-off&lt;/li&gt;
&lt;li&gt;A product name you keep mistyping&lt;/li&gt;
&lt;li&gt;A short sentence that has already been reviewed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep saved wording current. An outdated template can reproduce an outdated instruction perfectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use autocomplete while the sentence is still taking shape
&lt;/h2&gt;

&lt;p&gt;Now consider a reply that begins:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks for the recording. The part I need to understand is…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next words depend on this particular conversation. There may be no useful fixed phrase to save.&lt;/p&gt;

&lt;p&gt;Autocomplete proposes a continuation while you write. DraftKey provides that kind of help in compatible Mac text fields, with inference running locally.&lt;/p&gt;

&lt;p&gt;You can keep typing to ignore a suggestion. Its &lt;a href="https://www.getdraftkey.com/ai-autocomplete-mac" rel="noopener noreferrer"&gt;current autocomplete documentation&lt;/a&gt; also describes accepting one word at a time, which is useful when the beginning fits but the rest takes the sentence somewhere you didn’t intend. The shortcut depends on your autocomplete settings.&lt;/p&gt;

&lt;p&gt;You still need to supply the substance. A plausible continuation doesn’t establish what caused a bug or what a customer needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use a rewrite when the draft exists and the wording needs work
&lt;/h2&gt;

&lt;p&gt;Suppose you have this rough request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you record what happens? Start before clicking export and include the error message because that’s the bit we need.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here, the task is to change an existing draft. A specific instruction might be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make this a polite, concise support reply. Keep the request to start recording before clicking Export and include the error message.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;a href="https://www.getdraftkey.com/rewrite-text-mac" rel="noopener noreferrer"&gt;DraftKey’s Writing tools&lt;/a&gt;, you can work with the current paragraph or select a smaller passage. Review the proposed version, edit it if necessary, then choose Insert.&lt;/p&gt;

&lt;p&gt;Selecting only the passage that needs work also makes the review smaller. You don’t need to ask for a new version of the entire email.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Combine them within one reply
&lt;/h2&gt;

&lt;p&gt;A support message can contain both fixed and changing parts.&lt;/p&gt;

&lt;p&gt;Use a saved phrase for a standard request. Write the case-specific explanation yourself, with autocomplete if it helps. Then select that explanation for a rewrite if its wording needs another pass.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saved phrase: ask for app and macOS versions&lt;/li&gt;
&lt;li&gt;Case-specific text: explain which part of the recording is needed&lt;/li&gt;
&lt;li&gt;Optional rewrite: make that explanation easier to follow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep any wording that must remain exact outside the selected passage. Review the assembled reply before sending it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before choosing another writing tool
&lt;/h2&gt;

&lt;p&gt;Look at three ordinary messages you write regularly. For each one, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Am I repeating the same words?&lt;/li&gt;
&lt;li&gt;Am I still composing the sentence?&lt;/li&gt;
&lt;li&gt;Have I written the message but need to improve its wording?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If most of the work is repeated text, start with Text Replacement. If the wording changes with each conversation, autocomplete or rewriting may be more relevant.&lt;/p&gt;

&lt;p&gt;DraftKey covers those latter tasks in compatible Mac text fields. It requires Apple Silicon and macOS 14 or later. AI requires at least 8 GB of memory, an approximately 1.12 GB model download, and at least 2.2 GB of free space plus room for the app. A license is required.&lt;/p&gt;

&lt;p&gt;Its writing inference runs on your Mac; model downloads, licensing and updates still involve network services. Compatibility depends on the editor’s Accessibility support, and some custom editors and secure fields are excluded.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://www.getdraftkey.com/download" rel="noopener noreferrer"&gt;review DraftKey’s requirements and installation guide&lt;/a&gt; before deciding whether it fits your writing workflow.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>productivity</category>
      <category>writing</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
