<?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: imper</title>
    <description>The latest articles on Forem by imper (@imper_7cde72b79d2529291ec).</description>
    <link>https://forem.com/imper_7cde72b79d2529291ec</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%2F3812341%2F4a3e352b-d42f-4efe-bb2c-23e8703e87bb.jpg</url>
      <title>Forem: imper</title>
      <link>https://forem.com/imper_7cde72b79d2529291ec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/imper_7cde72b79d2529291ec"/>
    <language>en</language>
    <item>
      <title>/disconnect for opencode — a tiny TUI plugin I wish existed before I made it</title>
      <dc:creator>imper</dc:creator>
      <pubDate>Mon, 18 May 2026 17:23:30 +0000</pubDate>
      <link>https://forem.com/imper_7cde72b79d2529291ec/disconnect-for-opencode-a-tiny-tui-plugin-i-wish-existed-before-i-made-it-20j9</link>
      <guid>https://forem.com/imper_7cde72b79d2529291ec/disconnect-for-opencode-a-tiny-tui-plugin-i-wish-existed-before-i-made-it-20j9</guid>
      <description>&lt;p&gt;I use &lt;a href="https://opencode.ai" rel="noopener noreferrer"&gt;opencode&lt;/a&gt; as my daily TUI coding agent. It's good. But there's one thing that kept biting me.&lt;/p&gt;

&lt;p&gt;When you want to remove a single provider — say you rotated a key and want the old entry gone — opencode doesn't ship a clean way to do it. The choices are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Re-auth every provider from scratch&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;~/.local/share/opencode/auth.json&lt;/code&gt; in vim and pray the trailing comma survives&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I did option 2 maybe four times before I got annoyed enough to write a plugin.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode plugin opencode-tui-utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inside the TUI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/disconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It opens opencode's own provider picker — the same dialog component the rest of the TUI already uses — so it doesn't feel like a bolted-on script. You pick the provider you want gone, confirm, done.&lt;/p&gt;

&lt;p&gt;Token values are never printed. Only provider names and auth types are shown, so you can run it in a recorded session without paranoia.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other commands
&lt;/h2&gt;

&lt;p&gt;Since I was writing the plugin loader anyway, I added the three other slash commands I kept wanting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/lsp-toggle&lt;/code&gt; — flip LSP on/off without restarting the TUI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/websearch-toggle&lt;/code&gt; — same for web search&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/tool-status&lt;/code&gt; — print what's currently enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The toggle commands update your shell profile and prompt for a restart, which matches opencode's existing pattern for launch-gated flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a plugin instead of a fork
&lt;/h2&gt;

&lt;p&gt;opencode's plugin system is just JSON: drop the package into &lt;code&gt;~/.config/opencode/tui.json&lt;/code&gt; under &lt;code&gt;"plugin"&lt;/code&gt;, restart, the slash command shows up in the palette next to the built-ins. No fork to maintain, no rebase pain when opencode updates.&lt;/p&gt;

&lt;p&gt;This also means adding command #5 is mechanical — there's a shared API wrapper and the loader handles registration. If you've got a slash command you keep missing in opencode, &lt;a href="https://github.com/Blue-B/opencode-tui-utils/blob/main/CONTRIBUTING.md#command-ideas-up-for-grabs" rel="noopener noreferrer"&gt;there's a "command ideas up for grabs" section in CONTRIBUTING.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to find it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/Blue-B/opencode-tui-utils" rel="noopener noreferrer"&gt;https://github.com/Blue-B/opencode-tui-utils&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;opencode-tui-utils&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;License: MIT&lt;/li&gt;
&lt;li&gt;Inspired by &lt;a href="https://github.com/anomalyco/opencode/issues/10494" rel="noopener noreferrer"&gt;opencode issue #10494&lt;/a&gt;, where someone had asked for the disconnect flow&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Built by &lt;strong&gt;&lt;a href="https://github.com/Blue-B" rel="noopener noreferrer"&gt;@Blue-B&lt;/a&gt;&lt;/strong&gt; — happy to take feedback, especially from anyone using opencode in a workflow I haven't thought of.&lt;/p&gt;

</description>
      <category>opencode</category>
      <category>cli</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>One install command wires 8 CLI coding agents to a shared stealth Chromium</title>
      <dc:creator>imper</dc:creator>
      <pubDate>Mon, 18 May 2026 16:58:37 +0000</pubDate>
      <link>https://forem.com/imper_7cde72b79d2529291ec/one-install-command-wires-8-cli-coding-agents-to-a-shared-stealth-chromium-3e50</link>
      <guid>https://forem.com/imper_7cde72b79d2529291ec/one-install-command-wires-8-cli-coding-agents-to-a-shared-stealth-chromium-3e50</guid>
      <description>&lt;p&gt;I use a bunch of CLI coding agents — Claude Code, Codex CLI, pi, opencode, Gemini CLI, Kiro, Amp, Crush — and every one of them shipped its own headless Chromium that gets blocked by basic bot checks (&lt;code&gt;navigator.webdriver&lt;/code&gt;, missing plugins, the usual headless fingerprints).&lt;/p&gt;

&lt;p&gt;The fix itself isn't novel. Patched stealth Chromium has existed for years. The annoying part is wiring it into each agent — every one wants its config in a different file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code → &lt;code&gt;CLAUDE.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Codex CLI / opencode → &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Gemini CLI → &lt;code&gt;GEMINI.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Kiro → &lt;code&gt;.kiro/steering/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Amp / Crush → their own dotfiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and all of them need to point at the same browser binary.&lt;/p&gt;

&lt;p&gt;So I spent a weekend writing a one-command installer that does the wiring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Blue-B/browser-harness-kit
&lt;span class="nb"&gt;cd &lt;/span&gt;browser-harness-kit
bash install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It detects which of the 8 agents you have installed, writes the right rule file for each, and creates a shared &lt;code&gt;~/.playwright/cli.config.json&lt;/code&gt; so they all hit the same stealth Chromium through &lt;a href="https://www.npmjs.com/package/pi-playwright" rel="noopener noreferrer"&gt;pi-playwright&lt;/a&gt; as the CLI runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Every agent drives a real headed Chromium that passes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;navigator.webdriver&lt;/code&gt; check&lt;/li&gt;
&lt;li&gt;plugin / mimeType length checks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;window.chrome&lt;/code&gt; shape&lt;/li&gt;
&lt;li&gt;full &lt;a href="https://bot.sannysoft.com" rel="noopener noreferrer"&gt;bot.sannysoft.com&lt;/a&gt; matrix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Screenshot of the pass matrix and verify output are in the &lt;a href="https://github.com/Blue-B/browser-harness-kit" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it isn't
&lt;/h2&gt;

&lt;p&gt;This repo is integration glue only. It does NOT bundle or redistribute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[CloakBrowser]&lt;a href="https://www.reddit.com/r/SideProject/comments/1tgrmo3(https://github.com/CloakHQ/CloakBrowser)" rel="noopener noreferrer"&gt;https://www.reddit.com/r/SideProject/comments/1tgrmo3(https://github.com/CloakHQ/CloakBrowser)&lt;/a&gt;&lt;/strong&gt; — the actual stealth Chromium build (linked, not bundled)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playwright + &lt;a href="https://www.npmjs.com/package/pi-playwright" rel="noopener noreferrer"&gt;pi-playwright&lt;/a&gt;&lt;/strong&gt; — the runner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 8 agents themselves&lt;/strong&gt; — each lives at its own home&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just the wiring between them, so I don't have to redo it on every fresh box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm sharing it
&lt;/h2&gt;

&lt;p&gt;I wrote this for my own setup. After the third time pasting the same config snippets into a new machine I figured someone else would save a weekend.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/Blue-B/browser-harness-kit" rel="noopener noreferrer"&gt;https://github.com/Blue-B/browser-harness-kit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tested on WSL2 + Ubuntu 24.04 + Node 22&lt;/li&gt;
&lt;li&gt;License: MIT&lt;/li&gt;
&lt;li&gt;Reddit discussion: &lt;a href="https://www.reddit.com/r/SideProject/comments/1tgrmo3" rel="noopener noreferrer"&gt;https://www.reddit.com/r/SideProject/comments/1tgrmo3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to take feedback, or PRs adding adapters for CLI agents I haven't covered. If your agent reads from yet another rule file, drop an issue.&lt;/p&gt;




&lt;p&gt;Built by &lt;strong&gt;&lt;a href="https://github.com/Blue-B" rel="noopener noreferrer"&gt;@Blue-B&lt;/a&gt;&lt;/strong&gt; — find more side projects on my GitHub.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cli</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Building an offline subtitle extractor with whisper.cpp and Electron</title>
      <dc:creator>imper</dc:creator>
      <pubDate>Sun, 08 Mar 2026 02:37:46 +0000</pubDate>
      <link>https://forem.com/imper_7cde72b79d2529291ec/building-an-offline-subtitle-extractor-with-whispercpp-and-electron-44k2</link>
      <guid>https://forem.com/imper_7cde72b79d2529291ec/building-an-offline-subtitle-extractor-with-whispercpp-and-electron-44k2</guid>
      <description>&lt;p&gt;I watch a lot of foreign language content - anime, K-dramas, tech talks - and getting subtitles was always a pain. Upload to a random website, hit the daily limit, try another one, or install Python and figure out whisper's CLI.&lt;/p&gt;

&lt;p&gt;So over the past few months I've been building a desktop app that handles the whole pipeline locally.&lt;/p&gt;

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

&lt;p&gt;You drop a video file in, pick a whisper model size, and it spits out an SRT subtitle file. Optionally you can translate the subtitles using one of several engines.&lt;/p&gt;

&lt;p&gt;The speech-to-text runs via &lt;strong&gt;whisper.cpp&lt;/strong&gt; so everything stays on your machine. No uploads, no API calls for the transcription part. If you have an NVIDIA GPU it automatically uses CUDA, otherwise it falls back to CPU - this was one of the trickier parts to get right.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F0bixenk40upft6zx7vbk.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.amazonaws.com%2Fuploads%2Farticles%2F0bixenk40upft6zx7vbk.png" alt="App Screenshot" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech decisions
&lt;/h2&gt;

&lt;p&gt;I went with &lt;strong&gt;Electron + Node.js&lt;/strong&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-platform (though I'm mainly targeting Windows right now)&lt;/li&gt;
&lt;li&gt;Easy to bundle whisper.cpp binaries and ffmpeg&lt;/li&gt;
&lt;li&gt;The UI is just HTML/CSS/JS so iteration is fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For whisper.cpp integration, the app spawns it as a child process with the right flags depending on whether CUDA is available. Model files (GGML format) auto-download on first run into a local &lt;code&gt;_models/&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translation engines
&lt;/h2&gt;

&lt;p&gt;Translation is optional. Currently supported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MyMemory&lt;/strong&gt; - free, no API key, ~50K chars/day&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepL&lt;/strong&gt; - free tier 500K chars/month, needs API key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI GPT&lt;/strong&gt; - paid, good quality for nuanced translations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini&lt;/strong&gt; - Google's API, generous free tier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app chunks subtitle text and sends it in batches to avoid rate limits. Each engine has its own quirks with language codes so there's a mapping layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  v1.4.0 changes
&lt;/h2&gt;

&lt;p&gt;Just pushed the latest update which adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic GPU/CPU fallback detection&lt;/li&gt;
&lt;li&gt;Bundled ffprobe-static (no more separate ffmpeg install)&lt;/li&gt;
&lt;li&gt;Better DeepL language code mapping&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;It's packaged as a portable .exe - no install needed, just extract and run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Blue-B/WhisperSubTranslate" rel="noopener noreferrer"&gt;WhisperSubTranslate&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;License&lt;/strong&gt;: GPL-3.0&lt;/p&gt;

&lt;p&gt;If you're working on something similar or have suggestions, I'd love to hear about it.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>electron</category>
      <category>whisper</category>
    </item>
  </channel>
</rss>
