<?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: dinesh0666</title>
    <description>The latest articles on DEV Community by dinesh0666 (@dinesh0666).</description>
    <link>https://dev.to/dinesh0666</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%2F162392%2F0314e10b-166f-447d-a08c-261196595017.png</url>
      <title>DEV Community: dinesh0666</title>
      <link>https://dev.to/dinesh0666</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dinesh0666"/>
    <language>en</language>
    <item>
      <title>I built a VS Code extension that blocks AI so you can actually learn to code</title>
      <dc:creator>dinesh0666</dc:creator>
      <pubDate>Tue, 24 Mar 2026 09:23:46 +0000</pubDate>
      <link>https://dev.to/dinesh0666/i-built-a-vs-code-extension-that-blocks-ai-so-you-can-actually-learn-to-code-875</link>
      <guid>https://dev.to/dinesh0666/i-built-a-vs-code-extension-that-blocks-ai-so-you-can-actually-learn-to-code-875</guid>
      <description>&lt;h1&gt;
  
  
  🧘 I Built a VS Code Extension That Forces You to Code Without AI
&lt;/h1&gt;

&lt;p&gt;We've all been there — you're stuck on a problem, so you hit &lt;code&gt;Cmd+I&lt;/code&gt;, Copilot spits out 40 lines, and you paste it in. Problem "solved." But did &lt;em&gt;you&lt;/em&gt; solve it?&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Raw Code Focus&lt;/strong&gt; — a VS Code extension that completely blocks all AI assistance for a timed session, so you can actually practice thinking through problems yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;When you start a session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚫 &lt;strong&gt;Copilot&lt;/strong&gt; inline completions, Next Edit Suggestions, and Chat are all disabled&lt;/li&gt;
&lt;li&gt;🚫 &lt;strong&gt;TabNine&lt;/strong&gt;, &lt;strong&gt;Codeium&lt;/strong&gt;, and VS Code's built-in inline suggestions are off&lt;/li&gt;
&lt;li&gt;🚫 The "Generate code ⌘I" hint is hidden&lt;/li&gt;
&lt;li&gt;⏱ A &lt;strong&gt;live countdown&lt;/strong&gt; sits in your status bar (turns yellow under 5 min)&lt;/li&gt;
&lt;li&gt;📊 Stats are tracked — streaks, total focus time, completion rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When time's up, &lt;strong&gt;everything is silently restored&lt;/strong&gt; to exactly how it was.&lt;/p&gt;




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

&lt;p&gt;I noticed I was reaching for Copilot before I'd even fully read the error. That's not learning — it's outsourcing thinking.&lt;/p&gt;

&lt;p&gt;Raw Code Focus gives you a guilt-free way to say &lt;em&gt;"for the next hour, it's just me and the compiler."&lt;/em&gt; Like going to the gym without a spotter for every rep.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tricky Part — Actually Blocking Copilot
&lt;/h2&gt;

&lt;p&gt;Turns out, just toggling one setting isn't enough. Copilot has multiple entry points:&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="nl"&gt;"github.copilot.editor.enableAutoCompletions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"github.copilot.enable"&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;"*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"github.copilot.inlineSuggest.enable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"github.copilot.nextEditSuggestions.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"inlineChat.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"chat.commandCenter.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"workbench.editor.empty.hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hidden"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And settings must be written to &lt;strong&gt;both Global and Workspace&lt;/strong&gt; targets — otherwise a workspace config can silently override the block.&lt;/p&gt;

&lt;p&gt;The extension also re-checks every 3 seconds and closes any chat panels that get reopened, so there's no sneaking around it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;Open the dashboard with &lt;code&gt;Raw Code Focus: Open Dashboard&lt;/code&gt; to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔥 Current and longest day streak&lt;/li&gt;
&lt;li&gt;⏱ Total accumulated focus time&lt;/li&gt;
&lt;li&gt;✅ Session completion rate&lt;/li&gt;
&lt;li&gt;📋 Recent session history&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Search &lt;strong&gt;&lt;code&gt;Raw Code Focus&lt;/code&gt;&lt;/strong&gt; in VS Code Extensions, or grab it directly:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://marketplace.visualstudio.com/items?itemName=DhineshKumar.raw-code-focus" rel="noopener noreferrer"&gt;marketplace.visualstudio.com/items?itemName=DhineshKumar.raw-code-focus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source code: &lt;a href="https://github.com/dinesh0666/ai-detox" rel="noopener noreferrer"&gt;github.com/dinesh0666/ai-detox&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Pomodoro-style session scheduler&lt;/li&gt;
&lt;li&gt;[ ] Custom session goals and notes&lt;/li&gt;
&lt;li&gt;[ ] Weekly stats summary notification&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Give it a try and let me know what you think! And if you have ideas for what else should be blocked (looking at you, ChatGPT side-by-side windows 👀), drop them in the comments.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Built a Claude Skill That Turns Any CSV Into an Executive Report — Here's How</title>
      <dc:creator>dinesh0666</dc:creator>
      <pubDate>Wed, 18 Mar 2026 14:03:02 +0000</pubDate>
      <link>https://dev.to/dinesh0666/i-built-a-claude-skill-that-turns-any-csv-into-an-executive-report-heres-how-37gg</link>
      <guid>https://dev.to/dinesh0666/i-built-a-claude-skill-that-turns-any-csv-into-an-executive-report-heres-how-37gg</guid>
      <description>&lt;p&gt;Every data person knows the drill.&lt;/p&gt;

&lt;p&gt;Someone drops a new dataset in your Slack. You open it, stare at 40 columns and 50,000 rows, and spend the next two hours doing what you always do — profiling nulls, checking distributions, writing up "here's what the data says" for the third time this month.&lt;/p&gt;

&lt;p&gt;That two hours? I automated it. With a Claude Skill.&lt;/p&gt;

&lt;p&gt;It's called &lt;strong&gt;DataStory&lt;/strong&gt; — upload any CSV or Excel file, and it produces a full executive data narrative report in seconds. Zero manual analysis. I open-sourced it at &lt;a href="https://github.com/dinesh0666/data-story" rel="noopener noreferrer"&gt;github.com/dinesh0666/data-story&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's exactly how it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is a Claude Skill?
&lt;/h2&gt;

&lt;p&gt;If you haven't seen these yet — Claude Skills are installable instruction sets (&lt;code&gt;SKILL.md&lt;/code&gt; files) that teach Claude a specific repeatable workflow. When you install a skill and then trigger it (by uploading a file, using certain phrases, etc.), Claude reads the skill definition and executes the workflow step by step.&lt;/p&gt;

&lt;p&gt;Think of it like a &lt;code&gt;.github/workflows&lt;/code&gt; YAML, but for AI-driven tasks instead of CI/CD.&lt;/p&gt;

&lt;p&gt;The pattern I've been exploring lately is &lt;strong&gt;Claude-in-Claude&lt;/strong&gt; — where the Claude artifact or skill itself calls the Anthropic API to do the heavy analytical lifting. The UI is Claude, the analyst is also Claude. It's surprisingly powerful.&lt;/p&gt;




&lt;h2&gt;
  
  
  The DataStory architecture
&lt;/h2&gt;

&lt;p&gt;Here's the full pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSV/XLSX file
    ↓
profile_data.py        ← pandas: shape, nulls, distributions, outliers
    ↓
JSON profile
    ↓
Anthropic API          ← claude-sonnet-4-20250514: narrative generation
    ↓
JSON narrative
    ↓
generate_report.js     ← docx npm: 9-section Word report
    ↓
datastory_&amp;lt;file&amp;gt;.docx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three scripts, one orchestrator shell, and a React artifact that does all of this in-browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Profile the data (Python)
&lt;/h2&gt;

&lt;p&gt;The first script — &lt;code&gt;profile_data.py&lt;/code&gt; — runs pure pandas profiling. No ML, no magic. Just stats.&lt;/p&gt;

&lt;p&gt;For every column, it outputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Numeric&lt;/strong&gt;: &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, &lt;code&gt;mean&lt;/code&gt;, &lt;code&gt;median&lt;/code&gt;, &lt;code&gt;std&lt;/code&gt;, &lt;code&gt;q1&lt;/code&gt;, &lt;code&gt;q3&lt;/code&gt;, &lt;code&gt;outlier_count&lt;/code&gt; (IQR method)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Categorical&lt;/strong&gt;: top 5 value frequencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datetime&lt;/strong&gt;: range in days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All&lt;/strong&gt;: null count, null %, unique count
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Outlier detection via IQR
&lt;/span&gt;&lt;span class="n"&gt;iqr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q3&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;q1&lt;/span&gt;
&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;iqr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;iqr&lt;/span&gt;
&lt;span class="n"&gt;outliers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Files over 10k rows get automatically sampled. CSV encoding failures fall back to &lt;code&gt;latin-1&lt;/code&gt;. The output is a clean JSON blob.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Generate the narrative (Claude API)
&lt;/h2&gt;

&lt;p&gt;This is where the Claude-in-Claude magic happens. I send the profile JSON to &lt;code&gt;claude-sonnet-4-20250514&lt;/code&gt; with &lt;code&gt;max_tokens: 4000&lt;/code&gt; and a strict system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a senior data analyst writing an executive data story report.
Given a statistical profile, return ONLY valid JSON with these keys:
executive_summary, dataset_overview, key_findings (array of 5),
anomalies, column_insights, data_quality_score (0-100),
data_quality_label, data_quality_assessment, recommended_next_steps.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A critical lesson from building this: &lt;strong&gt;always set &lt;code&gt;max_tokens&lt;/code&gt; high enough&lt;/strong&gt;. My first version used &lt;code&gt;1000&lt;/code&gt; and got &lt;code&gt;Unterminated string in JSON&lt;/code&gt; errors because the response was being cut off mid-object. Bumped it to &lt;code&gt;4000&lt;/code&gt; and it works cleanly.&lt;/p&gt;

&lt;p&gt;I also slim the profile before sending — only 3 sample rows and 3 top values per column — to keep the prompt tight without losing analytical context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slimProfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sample_rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sample_rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;top_values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top_values&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top_values&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;The JSON parsing is hardened too — strips markdown fences, falls back to regex-extracting the first &lt;code&gt;{...}&lt;/code&gt; block if Claude adds any preamble:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&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;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\{[\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unexpected response`&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;h2&gt;
  
  
  Step 3: Generate the report (Node.js + docx)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;generate_report.js&lt;/code&gt; script takes the combined profile + narrative and builds a 9-section Word document using the &lt;code&gt;docx&lt;/code&gt; npm package:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cover (title, filename, date)&lt;/li&gt;
&lt;li&gt;Executive Summary (callout box)&lt;/li&gt;
&lt;li&gt;Dataset Overview (stats table)&lt;/li&gt;
&lt;li&gt;Key Findings (numbered list)&lt;/li&gt;
&lt;li&gt;Anomalies &amp;amp; Concerns (amber callout — auto-skipped if empty)&lt;/li&gt;
&lt;li&gt;Column Profiles (full table: type, nulls, stats, outliers)&lt;/li&gt;
&lt;li&gt;Column Insights (bullet list)&lt;/li&gt;
&lt;li&gt;Data Quality Assessment (score + paragraph)&lt;/li&gt;
&lt;li&gt;Recommended Next Steps + Appendix&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A few &lt;code&gt;docx&lt;/code&gt; gotchas that burned me and might save you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ NEVER use WidthType.PERCENTAGE — breaks in Google Docs&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ Always use WidthType.DXA&lt;/span&gt;
&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WidthType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DXA&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ NEVER ShadingType.SOLID — renders as solid black&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ Always ShadingType.CLEAR&lt;/span&gt;
&lt;span class="nl"&gt;shading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;D6E4F0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShadingType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLEAR&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Tables need DUAL widths — both on table AND each cell&lt;/span&gt;
&lt;span class="c1"&gt;// Forgetting one causes rendering issues on some platforms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The React artifact (Claude-in-Claude in the browser)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;app/DataStory.jsx&lt;/code&gt; file does the whole pipeline in-browser — no backend, no server. Just a React component that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parses the uploaded file client-side using SheetJS (&lt;code&gt;xlsx&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Runs the profiling in pure JS (rewritten from the Python logic)&lt;/li&gt;
&lt;li&gt;Calls the Anthropic API directly from the browser&lt;/li&gt;
&lt;li&gt;Renders the full interactive report&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And critically — there's a &lt;strong&gt;Download report&lt;/strong&gt; button that generates a self-contained HTML file. Open it in any browser, hit Ctrl+P, save as PDF. That's your shareable report.&lt;/p&gt;

&lt;p&gt;The quality ring (SVG animated arc) was a fun touch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;circ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;circle&lt;/span&gt; &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"42"&lt;/span&gt; &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"42"&lt;/span&gt; &lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;strokeDasharray&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;circ&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;strokeLinecap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
  &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"rotate(-90 42 42)"&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stroke-dasharray 1s ease&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The SKILL.md
&lt;/h2&gt;

&lt;p&gt;The skill definition is what makes this installable in Claude.ai. The &lt;code&gt;description&lt;/code&gt; field is critical — it's how Claude decides when to trigger the skill:&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;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Auto-generates an executive data narrative report from any uploaded CSV or Excel file.&lt;/span&gt;
  &lt;span class="s"&gt;Trigger when user uploads a CSV, XLSX, or data file and wants analysis, a data story,&lt;/span&gt;
  &lt;span class="s"&gt;a data profile, a summary report, insights, anomaly detection, or an executive summary.&lt;/span&gt;
  &lt;span class="s"&gt;Also trigger when user says: "analyze this data", "what does this dataset say",&lt;/span&gt;
  &lt;span class="s"&gt;"generate a report from this file", "profile my data", "summarize this spreadsheet".&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The description needs to be &lt;em&gt;pushy&lt;/em&gt; — Claude tends to undertrigger skills if the description is too vague. List out the exact phrases people will use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CLI — full pipeline&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-ant-...
bash skill/scripts/run_datastory.sh sales_data.csv
&lt;span class="c"&gt;# → datastory_sales_data.docx&lt;/span&gt;

&lt;span class="c"&gt;# Just profile (no API key needed)&lt;/span&gt;
python3 skill/scripts/profile_data.py data.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; profile.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or just use the React artifact — drag and drop your file, done.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned building this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Claude-in-Claude pattern is underrated.&lt;/strong&gt; Having the artifact call the Anthropic API directly removes the need for any backend while still getting full model power. The browser becomes the compute layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skill descriptions are the whole triggering mechanism.&lt;/strong&gt; Spending 10 minutes writing a precise, phrase-rich description is worth more than spending hours on the skill logic if Claude never triggers it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data profiling is still a solved problem — the narrative is the hard part.&lt;/strong&gt; Pandas stats take seconds. Writing "what this means" used to take hours. That's the gap the LLM fills.&lt;/p&gt;




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

&lt;p&gt;I'm planning to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-sheet Excel support&lt;/strong&gt; — currently reads sheet 1 only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlation matrix&lt;/strong&gt; — for numeric columns, spot which variables move together&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time series detection&lt;/strong&gt; — if a datetime column exists, auto-generate trend narrative&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF output&lt;/strong&gt; — skip the browser print step&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/dinesh0666/data-story" rel="noopener noreferrer"&gt;github.com/dinesh0666/data-story&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you build something with it or find a bug, open an issue. And if this sparked an idea for your own Claude Skill — I'd love to see what you build.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>anthropic</category>
      <category>datascience</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>notion-flows: Your Notion Pages Are Your Automation Code</title>
      <dc:creator>dinesh0666</dc:creator>
      <pubDate>Tue, 10 Mar 2026 07:50:09 +0000</pubDate>
      <link>https://dev.to/dinesh0666/notion-flows-your-notion-pages-are-your-automation-code-31d5</link>
      <guid>https://dev.to/dinesh0666/notion-flows-your-notion-pages-are-your-automation-code-31d5</guid>
      <description>&lt;h2&gt;
  
  
  The idea that broke my brain a little
&lt;/h2&gt;

&lt;p&gt;What if your automation logic lived in Notion — not as a diagram, not as a config file, but as a plain-English page you just... edit?&lt;/p&gt;

&lt;p&gt;No redeploy. No YAML. No drag-and-drop node editors.&lt;/p&gt;

&lt;p&gt;Just:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a Notion page&lt;/li&gt;
&lt;li&gt;Write what you want in plain English&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;notion-flows run&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Claude reads your page and executes it against your Notion databases&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's &lt;strong&gt;notion-flows&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevyqv2nihg8fo2uthvf7.gif" 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%2Fevyqv2nihg8fo2uthvf7.gif" alt="notion-flows demo: Notion page registry, CLI list, and agentic run loop" width="900" height="560"&gt;&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;notion-flows is a TypeScript CLI that treats Notion pages as executable business logic.&lt;/p&gt;

&lt;p&gt;You have a &lt;strong&gt;registry page&lt;/strong&gt; in Notion. Each child page is a "flow" — a plain-English description of an automation. The CLI discovers those pages, sends them to Claude with Notion MCP tools, and Claude executes the instructions step by step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three example flows are included out of the box:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔥 Follow Up Cold Leads
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This flow finds leads who haven't been contacted in 7 or more days
and drafts personalized follow-up messages.

## Conditions
• Check Contacts DB for Status = "Lead" or "Prospect"
• Filter where Last Contacted is 7+ days ago
• If Deal Value over $2,000, tag as hot

## Actions
• Draft a warm, professional follow-up for each contact
• Update "Next Action" field with the draft message
• Update "Last Contacted" to today
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📝 Content Idea Generator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate 5 high-value content ideas for a TypeScript consulting business
targeting early-stage startups, add them to the Content Pipeline database,
and write a full first draft for the highest ROI idea.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📊 Weekly CEO Brief
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate a weekly executive brief by reading all databases,
then append it to this page.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These aren't config files. They're just text. You can rewrite them any time and the behavior changes on the next run.&lt;/p&gt;




&lt;h2&gt;
  
  
  The agentic loop
&lt;/h2&gt;

&lt;p&gt;The core of notion-flows is a Claude agentic loop with Notion MCP tools.&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;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCallCount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;claude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude-opus-4-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;notion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMCPTools&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  &lt;span class="c1"&gt;// query, create, update, append, get&lt;/span&gt;
    &lt;span class="nx"&gt;messages&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end_turn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;finalSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&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;toolResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for &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;block&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;notion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&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="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;toolResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;tool_use_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toolResults&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;Claude gets 5 Notion MCP tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;notion_query_database&lt;/code&gt; — filter &amp;amp; sort any database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;notion_create_page&lt;/code&gt; — create DB entries or sub-pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;notion_update_page&lt;/code&gt; — patch any page properties&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;notion_append_blocks&lt;/code&gt; — write rich content to a page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;notion_get_page&lt;/code&gt; — retrieve page properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After each run, the flow's own Notion page gets a run log appended automatically — timestamp, summary, and every action taken.&lt;/p&gt;




&lt;h2&gt;
  
  
  Terminal output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;⚡ notion-flows  v1.0.0

⚡ Executing: 🔥 Follow Up Cold Leads
   Source: https://notion.so/xxxxx
────────────────────────────────────────────────────────────
  💭 I'll query the Contacts database for leads not contacted recently...
  🔧 notion_query_database (1/30) ✓
  💭 Found 3 contacts — drafting follow-ups and tagging one as hot...
  🔧 notion_update_page (2/30) ✓
  🔧 notion_update_page (3/30) ✓
  🔧 notion_update_page (4/30) ✓
  🔧 notion_append_blocks (5/30) ✓

&lt;/span&gt;&lt;span class="gp"&gt;✅ Updated 3 contacts with follow-up drafts. Tagged 1 as hot ($&lt;/span&gt;4,200 deal&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;
────────────────────────────────────────────────────────────
📊 Run Summary
────────────────────────────────────────────────────────────
✓ Follow Up Cold Leads — 3 records, 8.4s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Watch mode
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsx src/cli.ts watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Polls every 60 seconds. If you edit a flow page in Notion, it automatically re-runs that flow. Your Notion page is your CI pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;notion-flows/
├── src/
│   ├── cli.ts                    # setup / list / run / watch
│   ├── types/index.ts            # Flow, FlowRunResult, NotionWrite
│   ├── runtime/notion.ts         # Notion client + MCP tools
│   ├── executor/flow-executor.ts # Agentic Claude loop
│   └── utils/setup.ts            # One-command workspace scaffolder
├── package.json
├── tsconfig.json
└── .env.example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript (ESM, strict)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@anthropic-ai/sdk&lt;/code&gt; — Claude &lt;code&gt;claude-opus-4-5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@notionhq/client&lt;/code&gt; — Notion REST API&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chalk&lt;/code&gt; + &lt;code&gt;ora&lt;/code&gt; — terminal UX&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dotenv&lt;/code&gt; + &lt;code&gt;zod&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Get started in 3 commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/dinesh0666/notion-flows
&lt;span class="nb"&gt;cd &lt;/span&gt;notion-flows
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Add NOTION_API_KEY and ANTHROPIC_API_KEY to .env&lt;/span&gt;

npx tsx src/cli.ts setup &amp;lt;your-notion-page-id&amp;gt;
&lt;span class="c"&gt;# Prints DB IDs → paste them into .env&lt;/span&gt;

npx tsx src/cli.ts run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;setup&lt;/code&gt; command scaffolds your entire workspace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates the &lt;strong&gt;⚡ notion-flows&lt;/strong&gt; registry page&lt;/li&gt;
&lt;li&gt;Creates &lt;strong&gt;Contacts&lt;/strong&gt;, &lt;strong&gt;Content Pipeline&lt;/strong&gt;, and &lt;strong&gt;Revenue&lt;/strong&gt; databases with full property schemas&lt;/li&gt;
&lt;li&gt;Seeds all 3 example flow pages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Every automation tool eventually forces you to learn its interface — a canvas, a config syntax, a DSL. notion-flows flips that: the interface is a Notion page, which you already know.&lt;/p&gt;

&lt;p&gt;You can share flows with non-technical teammates. They can edit the logic. You re-run the CLI. No PRs, no deployments.&lt;/p&gt;

&lt;p&gt;The Notion page IS the code.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled triggers&lt;/strong&gt; — the &lt;code&gt;trigger: schedule: 0 9 * * 1&lt;/code&gt; syntax is already parsed, just needs a cron runner wired up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-workspace support&lt;/strong&gt; — share flows between orgs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flow versioning&lt;/strong&gt; — Notion's page history is already your git log&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion dev.to challenge&lt;/a&gt;. Source code on &lt;a href="https://github.com/dinesh0666/notion-flows" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>notionchallenge</category>
      <category>notion</category>
      <category>typescript</category>
      <category>ai</category>
    </item>
    <item>
      <title>This VS Code Extension Gives Your Coding Session a Vibe Score (and Loses Its Mind at GOD MODE)</title>
      <dc:creator>dinesh0666</dc:creator>
      <pubDate>Tue, 03 Mar 2026 10:28:25 +0000</pubDate>
      <link>https://dev.to/dinesh0666/i-built-a-vs-code-extension-that-tracks-your-flow-state-in-real-time-and-hypes-you-up-2li6</link>
      <guid>https://dev.to/dinesh0666/i-built-a-vs-code-extension-that-tracks-your-flow-state-in-real-time-and-hypes-you-up-2li6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/mlh-built-with-google-gemini-02-25-26"&gt;Built with Google Gemini: Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built with Google Gemini
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VibeMeter&lt;/strong&gt; — a VS Code extension that elevates vibe coding. It watches how you code in real-time, tracks your flow state, fires achievements when you hit milestones, and calls in an AI coach to roast or praise your session when you need it.&lt;/p&gt;

&lt;p&gt;Here's what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Live Vibe Score (0–100)&lt;/strong&gt; — computed from your typing speed, saves, active errors, and idle time. Updates every 5 seconds.&lt;/li&gt;
&lt;li&gt;🔥 &lt;strong&gt;Flow State Levels&lt;/strong&gt; — six levels that evolve as you code: 😴 Dead Inside → 🥶 Cold Start → 🧘 Vibing → 🌊 In the Flow → 🔥 On Fire → ⚡ GOD MODE&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Achievement System&lt;/strong&gt; — 14 unlockable achievements that fire as toast notifications mid-session ("Keyboard Destroyer", "Zone Resident", "GOD MODE", "The Comeback")&lt;/li&gt;
&lt;li&gt;🤖 &lt;strong&gt;AI Vibe Coach&lt;/strong&gt; — powered by Google Gemini. Sends your session stats + active code context and returns a witty, personalised coaching message&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Live Dashboard&lt;/strong&gt; — animated hero display, colour-coded score bar, 6-stat grid, and an achievement wall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The status bar reacts immediately — reach GOD MODE and it cycles through &lt;code&gt;⚡ GOD MODE 💀 GOD MODE 🔥 GOD MODE&lt;/code&gt; at 450ms intervals. It's dramatic. That's the point.&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%2Fkfwbv9maoxqhtq1knlsb.gif" 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%2Fkfwbv9maoxqhtq1knlsb.gif" alt="VibeMeter Demo" width="720" height="439"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Core Idea: Measure Flow, Not Just Output
&lt;/h3&gt;

&lt;p&gt;Most productivity tools count lines of code, commits, or PRs. But those are lagging indicators. What actually matters mid-session is &lt;strong&gt;flow state&lt;/strong&gt; — that effortless, locked-in zone where the code just pours out.&lt;/p&gt;

&lt;p&gt;VibeMeter tries to detect that in real-time:&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;private&lt;/span&gt; &lt;span class="nf"&gt;computeScore&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&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;kpm&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;getKpm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;       &lt;span class="c1"&gt;// keystrokes in last 60s&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idle&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;getIdleSeconds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kpm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// typing speed: 0–60 pts (caps at 60 KPM)&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saves&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// save bonus: max 20 pts&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// error penalty: max –30 pts&lt;/span&gt;

    &lt;span class="k"&gt;if      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// 5+ min idle: big drop&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;5&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&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;It's not perfect science — it's a vibe. The algorithm rewards momentum and punishes stagnation, which is exactly what flow state is.&lt;/p&gt;




&lt;h3&gt;
  
  
  How Gemini Powers the AI Coach
&lt;/h3&gt;

&lt;p&gt;When you hit &lt;code&gt;⌘⇧C&lt;/code&gt; (Mac) or &lt;code&gt;Ctrl+Alt+C&lt;/code&gt; (Windows), the AI Vibe Coach fires. It:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Grabs your current session stats (score, KPM, saves, errors, flow time)&lt;/li&gt;
&lt;li&gt;Grabs up to 100 lines of code around your cursor&lt;/li&gt;
&lt;li&gt;Sends both to &lt;code&gt;gemini-2.0-flash&lt;/code&gt; with a prompt that makes it act as a witty senior engineer slash hype man&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The prompt engineering here was the most fun part. The brief to Gemini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a witty, hype, but genuinely smart AI coding coach.
You're the vibe check your dev session deserves — equal parts hype man and senior engineer.

Rules:
1. Comment on their vibe state — hype them if they're cooking, roast gently if they're slacking.
2. Make ONE sharp, specific observation about the visible code (naming, logic, structure, etc.).
3. End with a concrete power tip or fun challenge to level up their session.
Use emojis. Be that coach nobody asked for but everyone needed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is coaching that actually reads the room. At 80 KPM with zero errors, Gemini is hyping you. At 10 KPM with 6 errors and 4 minutes idle, it's dishing out a gentle roast and a push. Surprisingly motivating.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Achievement System
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;HypeMachine&lt;/code&gt; class listens to every &lt;code&gt;VibeEngine&lt;/code&gt; state update and checks 14 different conditions. Some are simple counters:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalKeystrokes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ks_1000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// "Keyboard Destroyer 🚀"&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flowMinutes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flow_30&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// "Zone Resident 🧘"&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;⚡&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;godmode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// "GOD MODE ⚡"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Others are more nuanced — the &lt;strong&gt;"The Comeback"&lt;/strong&gt; achievement tracks if you started at Dead Inside (😴) and climbed back to In the Flow (🌊) or higher in the same session. That one feels earned.&lt;/p&gt;

&lt;p&gt;Level changes also trigger hype messages:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;level_up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Level UP! Your vibe is evolving 🔥&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The vibes are immaculate rn ✨&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...],&lt;/span&gt;
    &lt;span class="na"&gt;level_down&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Took a micro-nap mid-sprint, eh? 😴&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vibe dipped a bit — shake it off 🎯&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...],&lt;/span&gt;
    &lt;span class="na"&gt;idle_nudge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hey… you alive in there? 👀&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The cursor is lonely. Come back 🖱️&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...],&lt;/span&gt;
    &lt;span class="na"&gt;godmode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;⚡ GOD MODE ACTIVATED. Absolute unit energy 💀&lt;/span&gt;&lt;span class="dl"&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;These fire as VS Code &lt;code&gt;showInformationMessage&lt;/code&gt; toasts — non-intrusive, fun, and just visible enough to deliver a little dopamine hit mid-session.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  1. Context Is Everything in AI Coaching
&lt;/h3&gt;

&lt;p&gt;My first version of the coach prompt sent only session stats — pure numbers. The responses were generic ("you're doing great!"). The moment I added the actual &lt;strong&gt;code around the cursor&lt;/strong&gt;, the responses became specific and genuinely useful. Gemini would call out a suspicious variable name, notice a deeply nested callback, or compliment clean destructuring. That specificity is what makes it feel like a real coach rather than a motivational poster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Give the model context, not just data. Numbers alone don't let it reason. Code + numbers lets it actually see what's happening.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Structured Prompts Beat Conversational Ones
&lt;/h3&gt;

&lt;p&gt;For the coach, I gave Gemini three explicit rules and told it to use emojis. Responses are now consistently punchy — 4–5 sentences, always ending with an actionable tip. Without that structure, responses were all over the place in length and tone.&lt;/p&gt;

&lt;p&gt;This is the same lesson I'd take into any AI feature: &lt;strong&gt;treat the prompt like an API contract, not a conversation starter.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Real-Time Event Architecture Is Different from Request-Response
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;VibeEngine&lt;/code&gt; fires a state update every 5 seconds via a VS Code &lt;code&gt;EventEmitter&lt;/code&gt;. The status bar and hype machine both subscribe to it. This reactive pattern — where multiple consumers listen to one source of truth — is fundamentally different from the request-response model most AI apps use.&lt;/p&gt;

&lt;p&gt;The interesting design challenge: the event fires frequently, but you don't want achievement toasts firing on every tick. The solution was tracking state &lt;em&gt;transitions&lt;/em&gt; (level changed? fire hype) rather than state &lt;em&gt;values&lt;/em&gt; (score is 85? always fire).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Idle Detection Is Harder Than It Sounds
&lt;/h3&gt;

&lt;p&gt;First version penalised idle time from &lt;code&gt;Date.now()&lt;/code&gt; at activation. So if you opened VS Code, didn't type for 30 seconds, and then started — you were already "Dead Inside" before writing a single character.&lt;/p&gt;

&lt;p&gt;Fix: only start the idle clock from the first keystroke. Sounds obvious in retrospect. Most UX bugs do.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. GOD MODE Is a Design Decision
&lt;/h3&gt;

&lt;p&gt;I could have called the top level "Expert" or "Peak Flow". I called it GOD MODE and gave it a cycling animation with skull emojis. That choice made people actually &lt;em&gt;want&lt;/em&gt; to reach it. Naming and personality in developer tools matters more than we admit.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Google Gemini Feedback
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: &lt;code&gt;gemini-2.0-flash&lt;/code&gt; returns coaching responses in 1–2 seconds. For a mid-session check-in, that's the right window — fast enough to feel real-time, not so fast it feels canned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Comprehension&lt;/strong&gt;: Sending raw TypeScript/Python/whatever-the-dev-is-writing and asking for a specific observation worked better than I expected. Gemini consistently gave code-level feedback, not just generic encouragement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tone Control&lt;/strong&gt;: The "equal parts hype man and senior engineer" brief held up well across different code quality levels. It rarely went off the rails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The SDK&lt;/strong&gt;: &lt;code&gt;model.generateContent(prompt)&lt;/code&gt; is genuinely as simple as it looks. The npm package just works.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where I Hit Friction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured Output Guarantee&lt;/strong&gt;: Even with explicit format instructions, Gemini occasionally adds a preamble ("Sure! Here's your vibe check:") before the actual content. A strict mode where you pass a schema and get guaranteed JSON back would eliminate all the regex fallback code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Streaming in This Context&lt;/strong&gt;: I wanted to stream the coaching output word-by-word into the output channel for a typewriter effect. The VS Code Output Channel API supports appending line by line, but wiring that together with Gemini's streaming API required more plumbing than it was worth. Better streaming examples for non-web (IDE/CLI) contexts would help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Visibility&lt;/strong&gt;: I still don't have a clear picture of token consumption per call. An easy &lt;code&gt;model.countTokens()&lt;/code&gt; surfaced in the quickstart would help developers think about token economy earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Honest Take
&lt;/h3&gt;

&lt;p&gt;For a real-time, session-aware developer tool, &lt;code&gt;gemini-2.0-flash&lt;/code&gt; is the right model. It's fast enough to not break flow, smart enough to give code-specific feedback, and cheap enough that I'm not watching a meter tick during development. The coaching feature went from "cool gimmick" to "thing I actually use" surprisingly quickly.&lt;/p&gt;

&lt;p&gt;The one capability I wish existed: a lightweight classification endpoint that could passively detect code patterns (AI-generated vs human written, or common anti-patterns) without needing full prompt round-trips. That would unlock a whole tier of always-on analysis.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persist achievements&lt;/strong&gt; to workspace storage so they survive restarts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vibe streaks&lt;/strong&gt; — track how many consecutive days you hit flow state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team leaderboard&lt;/strong&gt; — compare vibe scores across a team repo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom hype messages&lt;/strong&gt; — let devs write their own achievement toasts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publish to the VS Code Marketplace&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try It Now — Sideload the VSIX
&lt;/h3&gt;

&lt;p&gt;A pre-built VSIX is included in the repo so you can try it without cloning or compiling:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/dinesh0666/vibemeter/raw/main/vibemeter.vsix" rel="noopener noreferrer"&gt;Download &lt;strong&gt;vibemeter.vsix&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;In VS Code: &lt;code&gt;⌘⇧P&lt;/code&gt; → &lt;strong&gt;Extensions: Install from VSIX…&lt;/strong&gt; → pick the file&lt;/li&gt;
&lt;li&gt;Add your &lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;Gemini API key&lt;/a&gt; in &lt;strong&gt;Settings → VibeMeter → Gemini Api Key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Start coding — the vibe score appears in your status bar immediately&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or via the terminal: &lt;code&gt;code --install-extension vibemeter.vsix&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The real irony&lt;/strong&gt;: I spent a session deep in GOD MODE building the system that detects GOD MODE. The tool validated itself. 🔥&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dinesh0666/vibemeter" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt; | Built with Google Gemini 2.0 Flash ✨&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>geminireflections</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Terminal Tunes - A Beautiful CLI Music Player with YouTube Streaming</title>
      <dc:creator>dinesh0666</dc:creator>
      <pubDate>Mon, 09 Feb 2026 11:59:07 +0000</pubDate>
      <link>https://dev.to/dinesh0666/terminal-tunes-a-beautiful-cli-music-player-with-youtube-streaming-328l</link>
      <guid>https://dev.to/dinesh0666/terminal-tunes-a-beautiful-cli-music-player-with-youtube-streaming-328l</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;Terminal Tunes&lt;/strong&gt; - A feature-rich CLI music player that brings the joy of music to your terminal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎨 &lt;strong&gt;Real-time Audio Spectrum Visualizer&lt;/strong&gt; - Colorful frequency bars that dance to your music&lt;/li&gt;
&lt;li&gt;🎬 &lt;strong&gt;YouTube Streaming&lt;/strong&gt; - Stream music directly from YouTube URLs and playlists with smart loading animations&lt;/li&gt;
&lt;li&gt;📁 &lt;strong&gt;Local File Playback&lt;/strong&gt; - Play MP3, M4A, WAV, FLAC files and entire folders&lt;/li&gt;
&lt;li&gt;💾 &lt;strong&gt;Smart Playlists&lt;/strong&gt; - Create custom playlists mixing local files and YouTube URLs&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Live Progress Tracking&lt;/strong&gt; - Real-time progress bars with actual duration&lt;/li&gt;
&lt;li&gt;🎚️ &lt;strong&gt;Dynamic Volume Control&lt;/strong&gt; - Adjust volume instantly using mpv IPC without interrupting playback&lt;/li&gt;
&lt;li&gt;🔀 &lt;strong&gt;Shuffle &amp;amp; Repeat&lt;/strong&gt; - Full playback control with keyboard shortcuts&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Network Error Handling&lt;/strong&gt; - 15-second timeout with animated error displays for offline/connectivity issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The player features a beautiful terminal UI built with blessed and blessed-contrib, providing a Spotify-like experience right in your terminal.&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/dinesh0666/terminal-tunes" rel="noopener noreferrer"&gt;https://github.com/dinesh0666/terminal-tunes&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Video Demo
&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%2Fgithub.com%2Fdinesh0666%2Fterminal-tunes%2Fraw%2Fmain%2Fdemo.gif%3Fraw%3Dtrue" 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%2Fgithub.com%2Fdinesh0666%2Fterminal-tunes%2Fraw%2Fmain%2Fdemo.gif%3Fraw%3Dtrue" alt="Terminal Tunes Demo" width="720" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Main Player Interface:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─Audio Spectrum───────────────────────────────────────────────────┐
│  ▉ ▉ ▉ ▉     ▉ ▉ ▉ ▉ ▉     ▉ ▉ ▉                                │
│  Now Playing: Artist - Song Title                                 │
└──────────────────────────────────────────────────────────────────┘

┌─♪ Now Playing──────┐  ┌─≡ Playlist──────────────────┐
│ Song Title          │  │ 1. First Song               │
│                     │  │ 2. Second Song ▶            │
│ Artist: Unknown     │  │ 3. Third Song               │
│ Album: Unknown      │  └─────────────────────────────┘
│ Format: stream      │
│                     │
│ Status: ▶ Playing   │
└─────────────────────┘

┌─Progress [0:45 / 3:30]──────────────┐  ┌─Vol 80%───┐
└─────────────────────────────────────┘  └───────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;&lt;span class="c"&gt;# Install globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; terminal-tunes

&lt;span class="c"&gt;# Play local music&lt;/span&gt;
tt play ~/Music/ &lt;span class="nt"&gt;--shuffle&lt;/span&gt;

&lt;span class="c"&gt;# Stream from YouTube&lt;/span&gt;
tt play &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=VIDEO_ID"&lt;/span&gt;

&lt;span class="c"&gt;# Create custom playlist&lt;/span&gt;
tt playlist create my-mix
&lt;span class="c"&gt;# Mix local files and YouTube URLs interactively&lt;/span&gt;

&lt;span class="c"&gt;# Import YouTube playlist&lt;/span&gt;
tt youtube import &lt;span class="s2"&gt;"PLAYLIST_URL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;1. YouTube Streaming with Loading Animation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detects YouTube URLs automatically&lt;/li&gt;
&lt;li&gt;Fetches video metadata (title, duration) using yt-dlp&lt;/li&gt;
&lt;li&gt;Shows beautiful loading animation while buffering&lt;/li&gt;
&lt;li&gt;Displays real-time progress tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Custom Mixed Playlists:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create playlists combining local MP3s and YouTube URLs&lt;/li&gt;
&lt;li&gt;Interactive track addition with metadata validation&lt;/li&gt;
&lt;li&gt;Save, load, and manage playlists easily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Real-time Audio Visualization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;32-bar spectrum analyzer with color gradients&lt;/li&gt;
&lt;li&gt;Smooth animations synced with playback&lt;/li&gt;
&lt;li&gt;Modern Spotify-inspired design&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Building Terminal Tunes with GitHub Copilot CLI was transformative. Here's how it impacted my development:&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Rapid Prototyping
&lt;/h3&gt;

&lt;p&gt;Copilot helped me quickly scaffold the project structure, suggesting the perfect libraries (blessed, blessed-contrib) for terminal UI. It understood my intent to build a "CLI music player with visualizations" and recommended the entire tech stack including mpv for streaming and yt-dlp for YouTube metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  🐛 Problem-Solving Challenges
&lt;/h3&gt;

&lt;p&gt;One of the biggest challenges was &lt;strong&gt;process management&lt;/strong&gt; - preventing zombie processes when users pause/resume or change tracks. Copilot suggested multiple killing strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PID-based termination&lt;/li&gt;
&lt;li&gt;Process group killing&lt;/li&gt;
&lt;li&gt;Synchronous cleanup with &lt;code&gt;execSync&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fallback commands (killall, pkill, ps+awk)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This multi-layered approach solved audio playback conflicts that would have taken hours to debug manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Smart Suggestions
&lt;/h3&gt;

&lt;p&gt;When implementing YouTube streaming, Copilot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recommended using &lt;code&gt;yt-dlp&lt;/code&gt; for metadata fetching (actual duration, not static)&lt;/li&gt;
&lt;li&gt;Suggested process spawning with &lt;code&gt;afplay&lt;/code&gt; for local files vs &lt;code&gt;mpv&lt;/code&gt; for streams&lt;/li&gt;
&lt;li&gt;Helped implement loading animations and error handling for network issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎨 UI/UX Enhancements
&lt;/h3&gt;

&lt;p&gt;Copilot suggested adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Volume percentage labels on gauge widgets&lt;/li&gt;
&lt;li&gt;Loading animations using braille characters (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏)&lt;/li&gt;
&lt;li&gt;Network error displays with pulsing borders&lt;/li&gt;
&lt;li&gt;Force-clearing trackInfo content to prevent UI state bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📚 Code Organization
&lt;/h3&gt;

&lt;p&gt;It helped restructure 500+ lines of player logic into clean sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Process Management&lt;/li&gt;
&lt;li&gt;Playback Control
&lt;/li&gt;
&lt;li&gt;Visualization &amp;amp; Progress&lt;/li&gt;
&lt;li&gt;Playlist Management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With private methods (&lt;code&gt;_initializeState&lt;/code&gt;, &lt;code&gt;_cleanupZombieProcesses&lt;/code&gt;) and comprehensive JSDoc comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Time Saved
&lt;/h3&gt;

&lt;p&gt;What would have taken 2-3 weeks of research and trial-and-error was completed in days. Copilot's context-aware suggestions meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less time reading documentation&lt;/li&gt;
&lt;li&gt;Fewer bugs to debug&lt;/li&gt;
&lt;li&gt;More time on features, not boilerplate&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎯 Workflow Impact
&lt;/h3&gt;

&lt;p&gt;My typical workflow became:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a comment describing what I want&lt;/li&gt;
&lt;li&gt;Copilot suggests implementation&lt;/li&gt;
&lt;li&gt;Accept or modify suggestion&lt;/li&gt;
&lt;li&gt;Test and iterate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This conversational coding style felt natural and productive. I could focus on &lt;strong&gt;what&lt;/strong&gt; to build, while Copilot helped with &lt;strong&gt;how&lt;/strong&gt; to build it.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Technical Deep Dive
&lt;/h3&gt;

&lt;p&gt;Some impressive moments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suggested using &lt;code&gt;music-metadata&lt;/code&gt; for parsing MP3 duration&lt;/li&gt;
&lt;li&gt;Recommended EventEmitter pattern for player state management&lt;/li&gt;
&lt;li&gt;Proposed request IDs to prevent race conditions in async playback&lt;/li&gt;
&lt;li&gt;Helped implement YouTube playlist import with ytdl-core&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📈 Learning Opportunity
&lt;/h3&gt;

&lt;p&gt;Beyond just code generation, Copilot became a learning tool. It exposed me to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better Node.js process management techniques&lt;/li&gt;
&lt;li&gt;Terminal UI best practices with blessed&lt;/li&gt;
&lt;li&gt;YouTube metadata extraction patterns&lt;/li&gt;
&lt;li&gt;Efficient error handling for network operations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Node.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Framework:&lt;/strong&gt; blessed, blessed-contrib&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio Players:&lt;/strong&gt; afplay (macOS local), mpv (streaming)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouTube Integration:&lt;/strong&gt; yt-dlp, ytdl-core, youtube-search-api&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata Parsing:&lt;/strong&gt; music-metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Framework:&lt;/strong&gt; Commander.js&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone repository&lt;/span&gt;
git clone https://github.com/dinesh0666/terminal-tunes.git
&lt;span class="nb"&gt;cd &lt;/span&gt;terminal-tunes

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Install system dependencies&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;mpv yt-dlp  &lt;span class="c"&gt;# macOS&lt;/span&gt;

&lt;span class="c"&gt;# Run&lt;/span&gt;
npm start play ~/Music/ &lt;span class="nt"&gt;--shuffle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Impact &amp;amp; Future
&lt;/h2&gt;

&lt;p&gt;Terminal Tunes demonstrates that CLI tools don't have to be boring. With proper UI design and modern features like streaming, they can compete with GUI applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future Enhancements:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Real FFT audio analysis (currently simulated)&lt;/li&gt;
&lt;li&gt;Seek/scrubbing functionality&lt;/li&gt;
&lt;li&gt;Lyrics display integration&lt;/li&gt;
&lt;li&gt;Last.fm scrobbling support&lt;/li&gt;
&lt;li&gt;Cross-platform audio engine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot CLI transformed how I approach development. It's not just an autocomplete tool—it's a pair programming partner that understands context, suggests best practices, and helps solve complex problems. Building Terminal Tunes showed me that with the right tools, ambitious projects become achievable in record time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it yourself:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; terminal-tunes
tt play &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=dQw4w9WgXcQ"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Made with ❤️ for music lovers who live in the terminal&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/dinesh0666/terminal-tunes" rel="noopener noreferrer"&gt;github.com/dinesh0666/terminal-tunes&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tags:&lt;/strong&gt; #cli #music #nodejs #githubcopilot #terminal&lt;/p&gt;

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