<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eli_coding</title>
    <description>The latest articles on Forem by Eli_coding (@eli_coding).</description>
    <link>https://forem.com/eli_coding</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2548075%2F1f8062c1-5b7e-4181-ae3a-87ec3244a796.png</url>
      <title>Forem: Eli_coding</title>
      <link>https://forem.com/eli_coding</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eli_coding"/>
    <language>en</language>
    <item>
      <title>I Built a GitHub Action That Codes My Todoist Tasks While I Sleep</title>
      <dc:creator>Eli_coding</dc:creator>
      <pubDate>Thu, 30 Apr 2026 09:59:06 +0000</pubDate>
      <link>https://forem.com/eli_coding/i-built-a-github-action-that-codes-my-todoist-tasks-while-i-sleep-3a4p</link>
      <guid>https://forem.com/eli_coding/i-built-a-github-action-that-codes-my-todoist-tasks-while-i-sleep-3a4p</guid>
      <description>&lt;h2&gt;
  
  
  I Built a GitHub Action That Reads My Todoist, Codes the Task, and Marks It Done — Every 5 Hours
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Yes, 5 hours. No, it's not random. Keep reading.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Being a Solo Developer
&lt;/h2&gt;

&lt;p&gt;You know that feeling when you have a perfectly organized Todoist board, a cup of coffee, and absolutely zero motivation to actually open your code editor?&lt;/p&gt;

&lt;p&gt;Same.&lt;/p&gt;

&lt;p&gt;I'm a frontend engineer building a product on my own. Between my day job, content creation, and pretending I go to the gym, finding focus time to chip away at the backlog is... a challenge.&lt;/p&gt;

&lt;p&gt;So I did what any reasonable developer would do: I automated myself out of the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Idea: An Agent Queue
&lt;/h2&gt;

&lt;p&gt;I created a Todoist project called &lt;code&gt;Agent_Queue&lt;/code&gt;. Every time I have a small coding task — a refactor, a new component, a bug fix — I add it as a task with a description detailed enough for an AI to understand it.&lt;/p&gt;

&lt;p&gt;Then a GitHub Action wakes up, grabs the highest priority task, hands it to &lt;strong&gt;Claude Code&lt;/strong&gt;, lets it do its thing, commits the result to my &lt;code&gt;dev&lt;/code&gt; branch, and marks the task as complete in Todoist.&lt;/p&gt;

&lt;p&gt;I wake up in the morning with code written. Sometimes it's great. Sometimes I need to tweak it. But it's a starting point I didn't have to make myself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Every 5 Hours?
&lt;/h2&gt;

&lt;p&gt;Okay, storytime. 🍿&lt;/p&gt;

&lt;p&gt;Claude Code uses API tokens under the hood. Those tokens have usage limits — and if you're running Claude Code autonomously on a task, it can be... enthusiastic. Like, "rewrite-your-entire-codebase-because-why-not" enthusiastic.&lt;/p&gt;

&lt;p&gt;Running it every 5 hours gives the token usage time to breathe and reset between runs. It also means if something goes wrong (Claude goes rogue, commits chaos, the API hiccups), you only have one task's worth of damage to deal with — not a pile-up of 47 autonomous commits from overnight.&lt;/p&gt;

&lt;p&gt;Think of it as: &lt;strong&gt;one task, one session, one breath&lt;/strong&gt;. The 5-hour gap is the coffee break Claude doesn't know it's taking.&lt;/p&gt;

&lt;p&gt;Also, Anthropic's rate limits are real. Respect them. Your wallet will thank you.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Workflow
&lt;/h2&gt;

&lt;p&gt;Here's exactly how it works, step by step:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Trigger
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*/5&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;  &lt;span class="c1"&gt;# every 5 hours&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="c1"&gt;# or manually, when you're impatient&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;workflow_dispatch&lt;/code&gt; is your emergency override. Sometimes you fix something in Todoist and just want to run it NOW. I use this constantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Find the Project + Top Task
&lt;/h3&gt;

&lt;p&gt;The action hits the Todoist API, finds the &lt;code&gt;Agent_Queue&lt;/code&gt; project by name, fetches all tasks, and sorts by priority. In Todoist's API, &lt;strong&gt;P1 = priority 4&lt;/strong&gt; (yes, inverted — don't ask, it's just how it is).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fetch top priority task from Todoist&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;todoist&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;TODOIST_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TODOIST_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;TODOIST_PROJECT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Agent_Queue'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;PROJECTS=$(curl -s \&lt;/span&gt;
      &lt;span class="s"&gt;-H "Authorization: Bearer $TODOIST_API_KEY" \&lt;/span&gt;
      &lt;span class="s"&gt;https://api.todoist.com/api/v1/projects)&lt;/span&gt;

    &lt;span class="s"&gt;PROJECT_ID=$(echo "$PROJECTS" | python3 -c "&lt;/span&gt;
    &lt;span class="s"&gt;import json, sys, os&lt;/span&gt;
    &lt;span class="s"&gt;data = json.load(sys.stdin)&lt;/span&gt;
    &lt;span class="s"&gt;name = os.environ['TODOIST_PROJECT_NAME']&lt;/span&gt;
    &lt;span class="s"&gt;match = next((p for p in data if p['name'] == name), None)&lt;/span&gt;
    &lt;span class="s"&gt;print(match['id'] if match else '')&lt;/span&gt;
    &lt;span class="s"&gt;")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use inline Python to parse the JSON — no extra dependencies, no setup. It's a bit cursed but it works beautifully.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Run Claude Code
&lt;/h3&gt;

&lt;p&gt;This is the magic step. Claude Code runs in the terminal, reads the full codebase, and executes the task prompt autonomously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Claude Code&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g @anthropic-ai/claude-code&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Claude Code on task&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;TASK_PROMPT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.todoist.outputs.task_prompt }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;echo "$TASK_PROMPT" | claude -p \&lt;/span&gt;
      &lt;span class="s"&gt;--dangerously-skip-permissions \&lt;/span&gt;
      &lt;span class="s"&gt;--max-turns 10 \&lt;/span&gt;
      &lt;span class="s"&gt;--output-format stream-json \&lt;/span&gt;
      &lt;span class="s"&gt;--verbose 2&amp;gt;&amp;amp;1 | tee claude-output.log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few flags worth calling out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; — Claude Code normally asks for confirmation before doing things. In CI there's no one to confirm, so we skip it. Use this with respect.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--max-turns 10&lt;/code&gt; — puts a hard limit on how many back-and-forth steps Claude takes. Prevents infinite loops.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--output-format stream-json&lt;/code&gt; — logs everything as JSON stream so you can actually debug what happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Commit to Dev
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit any changes Claude made&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;git config user.name "Claude Agent"&lt;/span&gt;
    &lt;span class="s"&gt;git config user.email "claude-agent@users.noreply.github.com"&lt;/span&gt;
    &lt;span class="s"&gt;git add -A&lt;/span&gt;
    &lt;span class="s"&gt;if git diff --staged --quiet; then&lt;/span&gt;
      &lt;span class="s"&gt;echo "No file changes to commit."&lt;/span&gt;
    &lt;span class="s"&gt;else&lt;/span&gt;
      &lt;span class="s"&gt;git commit -m "chore: agent task - ${{ steps.todoist.outputs.task_content }}"&lt;/span&gt;
      &lt;span class="s"&gt;git push&lt;/span&gt;
    &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Claude made no file changes (maybe the task was already done, or it was a read-only task), the workflow exits cleanly. No empty commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Mark the Task Done in Todoist
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mark task as complete in Todoist&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.todoist.outputs.task_found == 'true' &amp;amp;&amp;amp; success()&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;TODOIST_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TODOIST_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;TASK_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.todoist.outputs.task_id }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;curl -s -X POST \&lt;/span&gt;
      &lt;span class="s"&gt;-H "Authorization: Bearer $TODOIST_API_KEY" \&lt;/span&gt;
      &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
      &lt;span class="s"&gt;"https://api.todoist.com/api/v1/tasks/$TASK_ID/close"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;success()&lt;/code&gt; condition is important — the task only gets marked as done if &lt;strong&gt;everything before it succeeded&lt;/strong&gt;. If Claude crashed, or the push failed, the task stays open and will be picked up again next cycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Secrets You Need
&lt;/h2&gt;

&lt;p&gt;Go to your repo → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Secrets and variables&lt;/strong&gt; → &lt;strong&gt;Actions&lt;/strong&gt; and add:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret&lt;/th&gt;
&lt;th&gt;Where to get it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TODOIST_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Todoist → Settings → Integrations → Developer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;console.anthropic.com&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Does It Work With Other Task Tools?
&lt;/h2&gt;

&lt;p&gt;Yes — and this is the part I love. The GitHub Actions structure is completely generic. All you need is a task tool with a REST API that can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;List tasks&lt;/strong&gt; (with some priority or ordering)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close/complete a task&lt;/strong&gt; by ID&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So you can swap Todoist for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linear&lt;/strong&gt; — great for teams, has a beautiful API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jira&lt;/strong&gt; — enterprise-friendly, more verbose but works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asana&lt;/strong&gt; — same concept, different endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion&lt;/strong&gt; — databases as task lists, totally doable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Issues&lt;/strong&gt; — meta as it gets, but it works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only thing you change is the &lt;code&gt;curl&lt;/code&gt; commands in the fetch and close steps. Everything else — the Claude Code runner, the git commit, the cron schedule — stays identical.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips From Running This for a Few Weeks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Write detailed task descriptions.&lt;/strong&gt; The task content becomes the prompt for Claude Code. "Fix bug" will get you nowhere. "The login page throws a TypeError when the user submits an empty email — fix the null check in &lt;code&gt;AuthForm.tsx&lt;/code&gt;" will get you a working fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the task description field, not just the title.&lt;/strong&gt; My workflow uses the description as the full prompt if it exists, falling back to the title. Give Claude context — file names, expected behaviour, what you've already tried.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with small tasks.&lt;/strong&gt; Don't throw "refactor the entire auth module" at it on day one. Start with "add a missing aria-label to the submit button in LoginForm.tsx". Build trust with the agent before giving it the keys to the kingdom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review before merging.&lt;/strong&gt; This commits to &lt;code&gt;dev&lt;/code&gt;, not &lt;code&gt;main&lt;/code&gt;. Always review what Claude produced before merging. It's usually good, sometimes great, occasionally baffling.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Vibe
&lt;/h2&gt;

&lt;p&gt;I built this because I believe the best tools are the ones that work while you sleep. Not in a "hustle culture" way — in a "your computer can do repetitive things so your brain doesn't have to" way.&lt;/p&gt;

&lt;p&gt;My Todoist board is now also my team standup. I write the task, the agent picks it up, and when I sit down in the morning there's a commit waiting for me to review. It's a weird and delightful workflow and I'm fully here for it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Full YAML
&lt;/h2&gt;

&lt;p&gt;The complete workflow file is below. Drop it in &lt;code&gt;.github/workflows/todoist-sync.yml&lt;/code&gt; and you're good to go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Agent Queue Runner&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*/5&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;run-agent-task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;24'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fetch top priority task from Todoist&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;todoist&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TODOIST_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TODOIST_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;TODOIST_PROJECT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Agent_Queue'&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;PROJECTS=$(curl -s \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Authorization: Bearer $TODOIST_API_KEY" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
            &lt;span class="s"&gt;https://api.todoist.com/api/v1/projects)&lt;/span&gt;

          &lt;span class="s"&gt;if [ -z "$PROJECTS" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Empty response from Todoist API."&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;PROJECT_ID=$(echo "$PROJECTS" | python3 -c "&lt;/span&gt;
          &lt;span class="s"&gt;import json, sys, os&lt;/span&gt;
          &lt;span class="s"&gt;data = json.load(sys.stdin)&lt;/span&gt;
          &lt;span class="s"&gt;projects = data.get('results', data) if isinstance(data, dict) else data&lt;/span&gt;
          &lt;span class="s"&gt;name = os.environ['TODOIST_PROJECT_NAME']&lt;/span&gt;
          &lt;span class="s"&gt;match = next((p for p in projects if p['name'] == name), None)&lt;/span&gt;
          &lt;span class="s"&gt;print(match['id'] if match else '')&lt;/span&gt;
          &lt;span class="s"&gt;")&lt;/span&gt;

          &lt;span class="s"&gt;if [ -z "$PROJECT_ID" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Project not found."&lt;/span&gt;
            &lt;span class="s"&gt;echo "task_found=false" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;TASKS=$(curl -s \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Authorization: Bearer $TODOIST_API_KEY" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
            &lt;span class="s"&gt;"https://api.todoist.com/api/v1/tasks?project_id=$PROJECT_ID")&lt;/span&gt;

          &lt;span class="s"&gt;TASK_JSON=$(echo "$TASKS" | python3 -c "&lt;/span&gt;
          &lt;span class="s"&gt;import json, sys&lt;/span&gt;
          &lt;span class="s"&gt;data = json.load(sys.stdin)&lt;/span&gt;
          &lt;span class="s"&gt;tasks = data.get('results', data) if isinstance(data, dict) else data&lt;/span&gt;
          &lt;span class="s"&gt;if not tasks:&lt;/span&gt;
              &lt;span class="s"&gt;print('')&lt;/span&gt;
              &lt;span class="s"&gt;exit()&lt;/span&gt;
          &lt;span class="s"&gt;tasks.sort(key=lambda t: t.get('priority', 1), reverse=True)&lt;/span&gt;
          &lt;span class="s"&gt;t = tasks[0]&lt;/span&gt;
          &lt;span class="s"&gt;prompt = t.get('description', '').strip() or t['content']&lt;/span&gt;
          &lt;span class="s"&gt;print(json.dumps({'id': t['id'], 'content': t['content'], 'prompt': prompt}))&lt;/span&gt;
          &lt;span class="s"&gt;")&lt;/span&gt;

          &lt;span class="s"&gt;if [ -z "$TASK_JSON" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "No tasks found."&lt;/span&gt;
            &lt;span class="s"&gt;echo "task_found=false" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;TASK_ID=$(echo "$TASK_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")&lt;/span&gt;
          &lt;span class="s"&gt;TASK_CONTENT=$(echo "$TASK_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['content'])")&lt;/span&gt;
          &lt;span class="s"&gt;TASK_PROMPT=$(echo "$TASK_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['prompt'])")&lt;/span&gt;

          &lt;span class="s"&gt;echo "task_found=true" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "task_id=$TASK_ID" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "task_content&amp;lt;&amp;lt;EOF" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "$TASK_CONTENT" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "EOF" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "task_prompt&amp;lt;&amp;lt;EOF" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "$TASK_PROMPT" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
          &lt;span class="s"&gt;echo "EOF" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Claude Code&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.todoist.outputs.task_found == 'true'&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g @anthropic-ai/claude-code&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Claude Code on task&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.todoist.outputs.task_found == 'true'&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;TASK_PROMPT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.todoist.outputs.task_prompt }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "$TASK_PROMPT" | claude -p \&lt;/span&gt;
            &lt;span class="s"&gt;--dangerously-skip-permissions \&lt;/span&gt;
            &lt;span class="s"&gt;--max-turns 10 \&lt;/span&gt;
            &lt;span class="s"&gt;--output-format stream-json \&lt;/span&gt;
            &lt;span class="s"&gt;--verbose 2&amp;gt;&amp;amp;1 | tee claude-output.log&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit any changes Claude made&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.todoist.outputs.task_found == 'true'&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config user.name "Claude Agent"&lt;/span&gt;
          &lt;span class="s"&gt;git config user.email "claude-agent@users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git add -A&lt;/span&gt;
          &lt;span class="s"&gt;if git diff --staged --quiet; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "No changes to commit."&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;git commit -m "chore: agent task - ${{ steps.todoist.outputs.task_content }}"&lt;/span&gt;
            &lt;span class="s"&gt;git push&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mark task as complete in Todoist&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.todoist.outputs.task_found == 'true' &amp;amp;&amp;amp; success()&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TODOIST_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TODOIST_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;TASK_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.todoist.outputs.task_id }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -s -X POST \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Authorization: Bearer $TODOIST_API_KEY" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
            &lt;span class="s"&gt;"https://api.todoist.com/api/v1/tasks/$TASK_ID/close"&lt;/span&gt;
          &lt;span class="s"&gt;echo "Task marked complete."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you build this or adapt it for Linear/Jira/Notion, let me know in the comments — I'd love to see what variations people come up with.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me on DEV &lt;a href="https://dev.to/eli_coding"&gt;@eli_coding&lt;/a&gt; and Instagram &lt;a href="https://instagram.com/eli_coding" rel="noopener noreferrer"&gt;@eli_coding&lt;/a&gt; for more of this kind of thing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>automation</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Keep Telling Claude the Same Things. So He Started Writing Them Down Himself.</title>
      <dc:creator>Eli_coding</dc:creator>
      <pubDate>Fri, 10 Apr 2026 11:24:23 +0000</pubDate>
      <link>https://forem.com/eli_coding/i-keep-telling-claude-the-same-things-so-he-started-writing-them-down-himself-1i9</link>
      <guid>https://forem.com/eli_coding/i-keep-telling-claude-the-same-things-so-he-started-writing-them-down-himself-1i9</guid>
      <description>&lt;p&gt;&lt;em&gt;A small moment that changed how I think about AI coding tools.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you've used Claude Code for more than a week, you know the pattern.&lt;/p&gt;

&lt;p&gt;You tell it something. It does it. Next session — gone. You tell it again. It does it. Next session — gone again.&lt;/p&gt;

&lt;p&gt;For me, it was always the same three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"Keep components dumb. Logic goes in services."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Use constants instead of string literals."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Every new service or helper needs a unit test."&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I said these things so many times they started to feel like a ritual. Open Claude, remind Claude, work, close Claude, repeat.&lt;/p&gt;

&lt;p&gt;Then one day I was watching the terminal output — and I saw something I didn't expect.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happened
&lt;/h2&gt;

&lt;p&gt;I was working on a mid-sized Angular project — forms, validators, lazy-loaded modules, the usual stuff.&lt;/p&gt;

&lt;p&gt;I'd just corrected Claude again on the dumb components rule. Nothing unusual. But instead of just applying the fix and moving on, I watched the terminal and noticed Claude was still running.&lt;/p&gt;

&lt;p&gt;It was writing files.&lt;/p&gt;

&lt;p&gt;Not code files. Not component files. Its own internal files.&lt;/p&gt;

&lt;p&gt;Right there in the console output, I could see it creating &lt;code&gt;memory.md&lt;/code&gt;, then &lt;code&gt;feedback_dumb_components.md&lt;/code&gt;, then &lt;code&gt;feedback_unit_tests.md&lt;/code&gt; — one by one.&lt;/p&gt;

&lt;p&gt;I hadn't asked for any of this.&lt;/p&gt;




&lt;h2&gt;
  
  
  The files it created
&lt;/h2&gt;

&lt;p&gt;Claude Code had built its own memory index and a set of structured feedback files — one for each rule it had learned from our sessions together.&lt;/p&gt;

&lt;p&gt;Here's the actual &lt;code&gt;memory.md&lt;/code&gt; it wrote:&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;# Memory Index&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Project: Users Management&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;project_users_management.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  — Angular frontend for Users Management; tabs, forms, validators
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Feedback: OnPush + forms&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;feedback_onpush_forms.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  — How to correctly update OnPush components when reactive form state changes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Feedback: No inline styles&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;feedback_no_inline_styles.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  — Never use style attribute in HTML; always use scss files
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Feedback: Dumb components + constants&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;feedback_dumb_components.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  — Components must be dumb/agnostic; logic in services/helpers; always use constants
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Feedback: Unit tests required&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;feedback_unit_tests.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  — Every new service, helper, or util file must have a .spec.ts unit test file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's what it wrote inside &lt;code&gt;feedback_dumb_components.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;name: Dumb components + constants rule
type: feedback

Components should be dumb and agnostic — they bind signals/inputs from services 
and delegate all logic elsewhere. Business logic, form orchestration, and state 
management belong in services or helper functions, not in component classes.

Never use string literals in TypeScript code where a constant exists or can be created.

Why: User explicitly requested this as a project rule.

How to apply:
&lt;span class="p"&gt;-&lt;/span&gt; Move any non-trivial logic (subscriptions, form manipulation, state derivation) 
  into a service or helper.
&lt;span class="p"&gt;-&lt;/span&gt; Before writing a string like 'address', 'postalCode', etc., check if a constant 
  already exists. If not, create one.
&lt;span class="p"&gt;-&lt;/span&gt; Components should only: inject services, expose signals/computed for the template, 
  and forward events.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;feedback_unit_tests.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;name: Unit tests required for services, helpers, and utils
type: feedback

Whenever a new service, helper, or utility file is created, a unit test file 
(.spec.ts) must also be created for it.

Why: User explicitly requested this as a project rule.

How to apply: After writing a new .service.ts, helper.ts, or utils.ts file, 
immediately create the corresponding .spec.ts file with at minimum a basic 
test suite skeleton covering the main logic.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It didn't just remember the rules. It documented &lt;em&gt;why&lt;/em&gt; they exist and &lt;em&gt;how to apply them&lt;/em&gt;. Like a junior dev writing up notes after a code review — except nobody told it to.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this moment stuck with me
&lt;/h2&gt;

&lt;p&gt;Watching it happen in real time in the terminal was different from finding files afterwards.&lt;/p&gt;

&lt;p&gt;I saw Claude finish applying my correction, then immediately pivot to: &lt;em&gt;"I should make sure I don't repeat this mistake."&lt;/em&gt; And then it built a system to prevent that.&lt;/p&gt;

&lt;p&gt;That's not autocomplete. That's not even a smart assistant. That's something closer to a collaborator that takes feedback seriously.&lt;/p&gt;

&lt;p&gt;We talk a lot about prompting — how to write better instructions, how to get better output. But what Claude did here is different. It turned &lt;em&gt;my feedback&lt;/em&gt; into &lt;em&gt;its own system&lt;/em&gt;, without being asked.&lt;/p&gt;




&lt;h2&gt;
  
  
  What changed in my workflow after this
&lt;/h2&gt;

&lt;p&gt;Since seeing this happen, I've started being more deliberate about corrections. When Claude makes a mistake I've seen before, instead of just fixing it in the moment, I now say:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"This is a pattern I always want you to follow. Make sure you remember this."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And it does. It updates its own memory files.&lt;/p&gt;

&lt;p&gt;The result is that my sessions now build on each other instead of starting from zero. The tool gets more useful the longer I work with it — which is exactly what you want from any collaborator.&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest take
&lt;/h2&gt;

&lt;p&gt;Claude Code still makes mistakes. It still occasionally ignores its own memory. It's not perfect.&lt;/p&gt;

&lt;p&gt;But watching it write those files in real time made me realize I'd been thinking about AI tools the wrong way. I was treating each session as isolated.&lt;/p&gt;

&lt;p&gt;Claude was treating them as a relationship.&lt;/p&gt;

&lt;p&gt;One of us was doing it right.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;10 years of Angular. Now learning from my own tools.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Follow &lt;a href="https://instagram.com/eli_coding" rel="noopener noreferrer"&gt;@eli_coding&lt;/a&gt; on Instagram for weekly posts on Angular, AI and real engineering.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ai</category>
      <category>claude</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I've Been a Frontend Engineer for 10 Years. Here's How Claude Changed My Workflow in 30 Days.</title>
      <dc:creator>Eli_coding</dc:creator>
      <pubDate>Tue, 07 Apr 2026 18:02:38 +0000</pubDate>
      <link>https://forem.com/eli_coding/ive-been-a-frontend-engineer-for-11-years-heres-how-claude-changed-my-workflow-in-30-days-2222</link>
      <guid>https://forem.com/eli_coding/ive-been-a-frontend-engineer-for-11-years-heres-how-claude-changed-my-workflow-in-30-days-2222</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical, honest account from an Angular developer who was skeptical — and then wasn't.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I'll be upfront: I was one of those engineers who rolled my eyes at AI coding tools.&lt;/p&gt;

&lt;p&gt;"It hallucinates," I'd say. "It writes mediocre code." "I can do this faster myself."&lt;/p&gt;

&lt;p&gt;10 years of Angular, TypeScript, RxJS, and hard-won debugging instincts will do that to you. You build confidence in your own way of working, and anything that disrupts it feels like noise.&lt;/p&gt;

&lt;p&gt;Then I gave Claude a real chance. Not a toy project. My actual work.&lt;/p&gt;

&lt;p&gt;This is what I found.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I was building
&lt;/h2&gt;

&lt;p&gt;I was working on a mid-sized Angular 17 app — signals, standalone components, the whole modern stack. The task: a complex data table with filtering, sorting, inline editing, and real-time updates via WebSockets.&lt;/p&gt;

&lt;p&gt;The kind of component that takes a full day to get right, and another day to test properly.&lt;/p&gt;

&lt;p&gt;I decided to treat Claude as a pairing partner for the whole thing, from architecture to final polish.&lt;/p&gt;




&lt;h2&gt;
  
  
  The part that surprised me most: it's not about autocomplete
&lt;/h2&gt;

&lt;p&gt;Before this experiment, I thought of AI coding tools as fancy autocomplete. Type a function signature, get a suggestion, accept or reject.&lt;/p&gt;

&lt;p&gt;That's not what Claude is.&lt;/p&gt;

&lt;p&gt;The real shift was using it as a &lt;strong&gt;thinking partner&lt;/strong&gt; — before writing a single line of code.&lt;/p&gt;

&lt;p&gt;I described the component requirements in plain language and asked: &lt;em&gt;"What are the tradeoffs between using a single datasource with BehaviorSubject vs leveraging Angular Signals with computed state here?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer was genuinely useful. Not because it was magic, but because it forced me to articulate my problem clearly — and then gave me a structured way to think through it. Like a good code review, before the code exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Concrete example: the filter logic
&lt;/h2&gt;

&lt;p&gt;Here's a real case. I needed to combine three independent filters — search text, status dropdown, and a date range — into a single reactive stream that the table datasource consumes.&lt;/p&gt;

&lt;p&gt;My first instinct was something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// My initial rough sketch&lt;/span&gt;
&lt;span class="nf"&gt;filterByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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;I described the full requirement to Claude and asked for an approach using Angular Signals. It proposed this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataTableComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nx"&gt;searchText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;statusFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;dateRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;filteredItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statusFilter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateRange&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchesSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchesStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchesDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;matchesSearch&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;matchesStatus&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;matchesDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&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;Honestly? This is close to what I would have written. But it took me 4 minutes instead of 25.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it saved me the most time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Boilerplate I hate writing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unit test stubs. Interface definitions from API response shapes. Error handling wrappers. I started pasting in API JSON responses and asking Claude to generate the TypeScript interface. Three seconds. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Explaining &lt;em&gt;why&lt;/em&gt; something is broken&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd paste a broken piece of code and describe the symptom — not ask for a fix, just ask for an explanation. The process of getting a structured explanation helped me spot the bug myself, faster than if I'd just stared at the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Writing tests for things I'd already built&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Claude is excellent at generating a first pass of &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;it&lt;/code&gt; blocks from a component's public API. They're not perfect, but they're a solid 70% starting point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it still falls short
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It doesn't know your codebase.&lt;/strong&gt; Without context, suggestions can be technically correct but architecturally wrong for your project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It can be confidently wrong.&lt;/strong&gt; Especially with Angular-specific APIs that changed in recent versions. Always verify anything touching the framework internals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It doesn't replace judgment.&lt;/strong&gt; That's still on me.&lt;/p&gt;




&lt;h2&gt;
  
  
  What actually changed in my workflow
&lt;/h2&gt;

&lt;p&gt;Three habits I've kept after 30 days:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Before writing a component&lt;/strong&gt;, I describe it to Claude and ask for an architecture sketch — not code, just the approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After writing a function&lt;/strong&gt;, I paste it in and ask: &lt;em&gt;"What edge cases am I not handling?"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For any PR I'm unsure about&lt;/strong&gt;, I ask Claude to review it before I submit.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The honest summary
&lt;/h2&gt;

&lt;p&gt;I'm faster. Not 10x — that's marketing speak. But meaningfully faster on the tasks that used to drain me.&lt;/p&gt;

&lt;p&gt;If you're an experienced frontend engineer who's been skeptical — I was too. Give it a real 30-day trial on actual work, not toy examples. That's where the value shows up.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me on Instagram &lt;a href="https://instagram.com/eli_coding" rel="noopener noreferrer"&gt;@eli_coding&lt;/a&gt; for weekly posts on Angular, AI and real engineering.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
