<?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: Keiran Haax</title>
    <description>The latest articles on Forem by Keiran Haax (@keiranhaax).</description>
    <link>https://forem.com/keiranhaax</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%2F3227274%2Fc8aa916d-bdd0-4b8b-b9ea-0f09c67c3133.jpg</url>
      <title>Forem: Keiran Haax</title>
      <link>https://forem.com/keiranhaax</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/keiranhaax"/>
    <language>en</language>
    <item>
      <title>mrktr: A Reseller Price Dashboard That Lives in Your Terminal</title>
      <dc:creator>Keiran Haax</dc:creator>
      <pubDate>Mon, 16 Feb 2026 05:31:29 +0000</pubDate>
      <link>https://forem.com/keiranhaax/mrktr-a-reseller-price-dashboard-that-lives-in-your-terminal-28l5</link>
      <guid>https://forem.com/keiranhaax/mrktr-a-reseller-price-dashboard-that-lives-in-your-terminal-28l5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;mrktr&lt;/strong&gt; is a terminal-based reseller price research dashboard built with Go, &lt;a href="https://github.com/charmbracelet/bubbletea" rel="noopener noreferrer"&gt;Bubble Tea&lt;/a&gt;, and &lt;a href="https://github.com/charmbracelet/lipgloss" rel="noopener noreferrer"&gt;Lip Gloss&lt;/a&gt;. It lets resellers compare prices across eBay, Mercari, and Amazon without leaving the terminal.&lt;/p&gt;

&lt;p&gt;I flip stuff on marketplaces sometimes, and I got tired of opening a dozen browser tabs just to compare prices and figure out if a deal is actually worth it after fees. So I built a dashboard that does all of that in one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;Type a product name (e.g., "iPhone 14 Pro"), hit Enter, and mrktr queries live search APIs to find listings across marketplaces. Results land in a sortable, filterable table showing platform, price, condition, and listing status, with a row-by-row reveal animation. A real-time statistics panel calculates min, max, average, median, P25/P75 percentiles, standard deviation, and coefficient of variation, complete with sparkline trend visualization. A built-in profit calculator lets you enter your cost and instantly see net margins after platform-specific fees (eBay's 13.25%, Mercari's 10%, Amazon's 15%, Facebook's 5%).&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-marketplace search&lt;/strong&gt; with a 3-provider fallback chain (Brave → Tavily → Firecrawl)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conservative query expansion&lt;/strong&gt; using a local TF-IDF product index for vague queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline predictive suggestions&lt;/strong&gt; with ghost text from search history and product catalog&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three stats views&lt;/strong&gt;: Summary, Distribution (histogram), and Market (per-platform breakdown)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profit calculator&lt;/strong&gt; with real platform fee structures and "best net platform" recommendation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSV/JSON export&lt;/strong&gt; and clipboard copy for individual listings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search history&lt;/strong&gt; persisted across sessions with relative timestamps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Animated intro&lt;/strong&gt;, row-by-row result reveals, value tweening between searches, and a reduce-motion accessibility toggle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vim-style navigation&lt;/strong&gt; (j/k, Tab cycling, / to search, c for calculator)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;The app follows the &lt;strong&gt;Elm Architecture (Model-View-Update)&lt;/strong&gt; pattern that Bubble Tea encourages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mrktr/
├── main.go          # Entry point, alt screen + mouse support
├── model.go         # Application state, message types, initialization
├── update.go        # All state transitions and side effects
├── view.go          # Pure rendering, no side effects
├── view_panels.go   # Panel render methods (search, results, stats, calc, history)
├── view_intro.go    # Animated ASCII art splash screen
├── styles.go        # Lip Gloss styles with adaptive light/dark colors
├── keys.go          # Keybinding definitions
├── history.go       # JSON-backed persistent search history
├── export.go        # CSV and JSON export logic
├── api/             # Search providers, price parser, query suggestions
│   ├── search.go    # Provider fallback chain coordinator
│   ├── brave.go     # Brave Search API provider
│   ├── tavily.go    # Tavily API provider
│   ├── firecrawl.go # Firecrawl API provider
│   ├── parse.go     # Regex price extraction + platform detection
│   └── suggest.go   # TF-IDF product index for query expansion
├── types/           # Shared domain types
│   ├── listing.go   # Listing, Statistics, ProfitCalculation
│   ├── fees.go      # Platform fee calculations
│   ├── sort.go      # Multi-field sorting
│   └── filter.go    # Platform/condition/status filtering
└── idea/            # Extended statistics and visualization
    ├── stats_model.go        # ExtendedStatistics with percentiles, histograms
    ├── stats_distribution.go # Histogram rendering
    ├── stats_market.go       # Per-platform stat breakdowns
    └── stats_animation.go    # Skeleton loading and value tweening
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The codebase is ~9,300 lines of Go across 40+ source files, with &lt;strong&gt;200+ tests&lt;/strong&gt; (118 test functions with table-driven subtests) covering parsers, statistics calculations, sorting/filtering, fee math, history persistence, animations, and UI state transitions.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/keiranhaax/mrktr" rel="noopener noreferrer"&gt;github.com/keiranhaax/mrktr&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Search results with real-time statistics and sparkline trend:&lt;/strong&gt;&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%2Fraw.githubusercontent.com%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-results.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%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-results.png" alt="mrktr search results" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profit calculator showing net margins after platform fees:&lt;/strong&gt;&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%2Fraw.githubusercontent.com%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-profit.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%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-profit.png" alt="mrktr profit calculator" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price distribution histogram view:&lt;/strong&gt;&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%2Fraw.githubusercontent.com%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-distribution.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%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-distribution.png" alt="mrktr distribution view" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Detail overlay with per-platform market breakdown:&lt;/strong&gt;&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%2Fraw.githubusercontent.com%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-detail.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%2Fkeiranhaax%2Fmrktr%2F7c9cb0a552319b5c6087e570cad568aba4316f09%2Fassets%2Fdashboard-detail.png" alt="mrktr detail and market view" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  User Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Launch&lt;/strong&gt;: &lt;code&gt;go run .&lt;/code&gt; from the &lt;code&gt;mrktr/&lt;/code&gt; directory (requires at least one API key: &lt;code&gt;BRAVE_API_KEY&lt;/code&gt;, &lt;code&gt;TAVILY_API_KEY&lt;/code&gt;, or &lt;code&gt;FIRECRAWL_API_KEY&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt;: Type a product name in the search panel. Inline ghost-text suggestions appear from your history and a built-in product catalog. Press &lt;code&gt;Tab&lt;/code&gt; to accept a suggestion or &lt;code&gt;Enter&lt;/code&gt; to search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browse Results&lt;/strong&gt;: Results appear row-by-row with an animation. Navigate with &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt; or arrow keys. Press &lt;code&gt;s&lt;/code&gt; to cycle sort fields (price, platform, condition, status), &lt;code&gt;S&lt;/code&gt; to reverse direction, &lt;code&gt;f&lt;/code&gt; to open the filter bar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyze Stats&lt;/strong&gt;: The statistics panel updates in real-time. Press &lt;code&gt;1&lt;/code&gt;/&lt;code&gt;2&lt;/code&gt;/&lt;code&gt;3&lt;/code&gt; to switch between Summary view (sparkline + percentiles), Distribution view (price histogram), and Market view (per-platform breakdowns).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate Profit&lt;/strong&gt;: Press &lt;code&gt;c&lt;/code&gt; to focus the profit calculator. Enter your cost and see net profit at min/avg/max prices after platform fees. Press &lt;code&gt;p&lt;/code&gt; to cycle platforms and compare fee structures. The "Best Net @ Avg" line tells you which platform gives the highest profit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act on Results&lt;/strong&gt;: Press &lt;code&gt;Enter&lt;/code&gt; to open the detail overlay, then &lt;code&gt;Enter&lt;/code&gt; again to open the listing URL in your browser. Press &lt;code&gt;y&lt;/code&gt; to copy the URL, &lt;code&gt;Y&lt;/code&gt; to copy the full listing, &lt;code&gt;e&lt;/code&gt; to export all results as CSV, or &lt;code&gt;E&lt;/code&gt; for JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History&lt;/strong&gt;: Previous searches are saved with timestamps and result counts. Press &lt;code&gt;Tab&lt;/code&gt; to the history panel, navigate with &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt;, and press &lt;code&gt;Enter&lt;/code&gt; to re-run a past search.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/keiranhaax/mrktr.git
&lt;span class="nb"&gt;cd &lt;/span&gt;mrktr

&lt;span class="c"&gt;# Set at least one API key&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BRAVE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-key-here"&lt;/span&gt;

&lt;span class="c"&gt;# Run&lt;/span&gt;
go run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;This project was built with AI assistance the whole way through, mainly &lt;strong&gt;GitHub Copilot CLI&lt;/strong&gt; and &lt;strong&gt;Claude Code&lt;/strong&gt;. Both run in the terminal, which made a big difference for a TUI project where I'm constantly switching between writing code and running &lt;code&gt;go run .&lt;/code&gt; to check the output.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the Tools Helped
&lt;/h3&gt;

&lt;p&gt;The whole thing went from nothing to ~9,300 lines with 200+ tests over a weekend. I don't think I could have done that without the CLI tools handling a lot of the repetitive parts. Here's what that actually looked like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaffolding.&lt;/strong&gt; I started by having the AI set up the Bubble Tea MVU structure: state in &lt;code&gt;model.go&lt;/code&gt;, transitions in &lt;code&gt;update.go&lt;/code&gt;, rendering in &lt;code&gt;view.go&lt;/code&gt;. It got the conventions right out of the gate (message types with &lt;code&gt;Msg&lt;/code&gt; suffix, &lt;code&gt;tea.Cmd&lt;/code&gt; for side effects, pure &lt;code&gt;View()&lt;/code&gt; functions), so I could jump straight to building features instead of wiring up boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API providers.&lt;/strong&gt; The three search providers (Brave, Tavily, Firecrawl) all follow the same shape: isolated function, gated on an env var, returning &lt;code&gt;[]types.Listing&lt;/code&gt;. I got the first one working manually, then AI helped stamp out the other two while adapting to each API's response format. The fallback chain in &lt;code&gt;api/search.go&lt;/code&gt; went through a few rounds of back-and-forth to get the error handling right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests.&lt;/strong&gt; 200+ tests is still a lot for a weekend project. I didn't write each one by hand. I'd write one or two table-driven tests to establish the pattern, then have the AI expand coverage with edge cases (malformed prices, missing fields, zero results, that kind of thing). The &lt;code&gt;api/parse_test.go&lt;/code&gt; and &lt;code&gt;types/&lt;/code&gt; test files are dense because of this. Golden snapshot tests for the stats panel rendering were AI-assisted too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animations.&lt;/strong&gt; The intro animation, sparkline rendering, gradient text, and profit bars all involve fiddly math (color interpolation, easing curves, tweening). I'd describe the visual effect I wanted and the AI would spit out the math. This was probably the area where AI saved the most time, because I'd have spent ages looking up interpolation formulas otherwise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactoring.&lt;/strong&gt; As the codebase grew, the AI helped extract the &lt;code&gt;api/&lt;/code&gt; package from inline code, pull types into &lt;code&gt;types/&lt;/code&gt;, and later split out the &lt;code&gt;idea/&lt;/code&gt; package for extended statistics. Being able to rename or move things across multiple files without breaking tests made it easy to keep the structure clean as I went.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Worked
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Staying in the terminal.&lt;/strong&gt; For a TUI project this was huge. I never had to context-switch out of my editor/shell to get help. Just ask, get an answer, keep going.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pattern replication.&lt;/strong&gt; Once I had one provider, one panel renderer, or one set of table-driven tests working, the AI could stamp out more of the same reliably. That's where a lot of the test coverage comes from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheap experiments.&lt;/strong&gt; Features like the TF-IDF query expansion, histogram view, and reduce-motion toggle started as "let's see if this works" ideas. AI made it cheap to try things, and the ones that worked became features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What the AI Couldn't Do for Me
&lt;/h3&gt;

&lt;p&gt;The design decisions were all mine. Which panels to include, how Tab should work (it cycles panels normally, but accepts search suggestions when you're in the search bar), what fees each platform charges, the visual hierarchy. The color palette (&lt;code&gt;#7D56F4&lt;/code&gt; → &lt;code&gt;#EA80FC&lt;/code&gt;), rounded vs. thick borders for active panels, adaptive colors for light/dark terminals: I picked all of that. AI can generate code fast, but it doesn't have opinions about what looks good.&lt;/p&gt;

&lt;p&gt;Same for the trickier architectural stuff. Keeping views pure, using generation counters to prevent stale animation ticks, cancelling in-flight searches when a new one starts: those decisions came from understanding how Bubble Tea actually works, not from prompting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Multiple Tools
&lt;/h3&gt;

&lt;p&gt;I used both Copilot CLI and Claude Code on this project, and they're good at different things. Copilot CLI was better for quick inline completions and shell commands. Claude Code was better for big multi-file refactors and cranking out test suites. Using both made sense.&lt;/p&gt;

&lt;p&gt;If you're building TUI apps, having an AI assistant in a terminal pane next to your running app is a really good setup. Worth trying if you haven't.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Built with:&lt;/strong&gt; &lt;a href="https://github.com/charmbracelet/bubbletea" rel="noopener noreferrer"&gt;Bubble Tea&lt;/a&gt; · &lt;a href="https://github.com/charmbracelet/lipgloss" rel="noopener noreferrer"&gt;Lip Gloss&lt;/a&gt; · &lt;a href="https://github.com/charmbracelet/bubbles" rel="noopener noreferrer"&gt;Bubbles&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
  </channel>
</rss>
