<?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: Stephen Lee</title>
    <description>The latest articles on Forem by Stephen Lee (@euphorie).</description>
    <link>https://forem.com/euphorie</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%2F3812611%2F702150c9-6f65-44b8-96da-379a8f81bb43.png</url>
      <title>Forem: Stephen Lee</title>
      <link>https://forem.com/euphorie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/euphorie"/>
    <language>en</language>
    <item>
      <title>I stopped building AI tools and started doing the tasks manually instead</title>
      <dc:creator>Stephen Lee</dc:creator>
      <pubDate>Fri, 03 Apr 2026 05:02:06 +0000</pubDate>
      <link>https://forem.com/euphorie/i-stopped-building-ai-tools-and-started-doing-the-tasks-manually-instead-5de4</link>
      <guid>https://forem.com/euphorie/i-stopped-building-ai-tools-and-started-doing-the-tasks-manually-instead-5de4</guid>
      <description>&lt;p&gt;I've been building AI products for a while now. Every time I thought I knew what to automate, I was wrong.&lt;/p&gt;

&lt;p&gt;So I tried something different. Instead of building another tool, I put up a simple page: describe a repetitive task, pay $5, I do it and email you the result.&lt;/p&gt;

&lt;p&gt;The tech stack is intentionally boring. Django backend, Stripe checkout, SQLite, static HTML frontend. No React. No AI. No accounts. The whole thing is under 500 lines of code.&lt;/p&gt;

&lt;p&gt;Why? Because the product isn't the software. The product is the dataset of tasks people submit. Every submission gets categorized: input type, output type, difficulty, whether AI failed at it, whether it's recurring.&lt;/p&gt;

&lt;p&gt;After enough submissions, patterns emerge. Those patterns become the first automated tools. The manual execution phase is just product discovery with real revenue.&lt;/p&gt;

&lt;p&gt;The site is euphorie.com if you want to see how minimal it is.&lt;/p&gt;

&lt;p&gt;Has anyone else tried using manual execution as a product discovery method? Curious what other approaches people have used to figure out what to build.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
    <item>
      <title>How I built a trust scoring hook for Claude Code</title>
      <dc:creator>Stephen Lee</dc:creator>
      <pubDate>Sun, 08 Mar 2026 08:03:30 +0000</pubDate>
      <link>https://forem.com/euphorie/how-i-built-a-trust-scoring-hook-for-claude-code-1465</link>
      <guid>https://forem.com/euphorie/how-i-built-a-trust-scoring-hook-for-claude-code-1465</guid>
      <description>&lt;p&gt;I've been using Claude Code for a while and realized I had zero visibility into what the agent was doing across sessions. Which tools it called, whether it touched files it shouldn't have, how many calls it made per task.&lt;/p&gt;

&lt;p&gt;So I built a hook that scores every session.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;The hook listens to three Claude Code events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PostToolUse&lt;/strong&gt; — records every tool call, checks it against an allowlist, flags protected path access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PreToolUse&lt;/strong&gt; — blocks tool calls that touch sensitive files like &lt;code&gt;.env&lt;/code&gt; or SSH keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop&lt;/strong&gt; — computes the final trust score and logs it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end of each session you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[authe.me] Trust Score: 92 (reliability=100 | scope=75 | cost=100)
[authe.me] tools=14 violations=1 failed=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The scoring
&lt;/h2&gt;

&lt;p&gt;Three dimensions, weighted into an overall score:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reliability (40%)&lt;/strong&gt; — what percentage of tool calls succeeded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope (35%)&lt;/strong&gt; — did the agent stay within your allowed tools and paths. Each violation drops the score by 25 points&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost (25%)&lt;/strong&gt; — how many tool calls were made. Under 20 is fine, over 100 starts flagging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hash chaining
&lt;/h2&gt;

&lt;p&gt;Every tool call event gets hashed with the previous hash, creating a chain. If someone tampers with the log, the chain breaks. Same concept as blockchain audit trails but much simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prev_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&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;prev_hash&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Protected path blocking
&lt;/h2&gt;

&lt;p&gt;The PreToolUse hook checks if Claude is about to read or edit a sensitive file. If it matches, the hook returns a deny decision with a reason that Claude receives as feedback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&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;hookSpecificOutput&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hookEventName&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="s"&gt;PreToolUse&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="s"&gt;permissionDecision&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="s"&gt;deny&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="s"&gt;permissionDecisionReason&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="s"&gt;authe.me policy: .env is a protected path.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude sees the denial reason and adjusts its plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.claude/hooks ~/.authe
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/autheme/claude-code-hook/main/authe-hook.py &lt;span class="nt"&gt;-o&lt;/span&gt; ~/.claude/hooks/authe-hook.py
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.claude/hooks/authe-hook.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the hook config to &lt;code&gt;~/.claude/settings.json&lt;/code&gt;:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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;"PostToolUse"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"hooks"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AUTHE_HOOK_EVENT=PostToolUse python3 ~/.claude/hooks/authe-hook.py"&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="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="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"hooks"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AUTHE_HOOK_EVENT=Stop python3 ~/.claude/hooks/authe-hook.py"&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="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="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="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;Single file, zero dependencies, pure Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Working on remote reporting so you can aggregate scores across sessions and agents. Also building an OpenClaw plugin that does the same thing for that ecosystem.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/autheme/claude-code-hook" rel="noopener noreferrer"&gt;github.com/autheme/claude-code-hook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with Claude. Would love feedback from anyone running Claude Code in production.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
    </item>
  </channel>
</rss>
