<?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: Anshu Pandey </title>
    <description>The latest articles on DEV Community by Anshu Pandey  (@echoanshu).</description>
    <link>https://dev.to/echoanshu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4215719%2Ff9c6df70-e00d-4188-a6b7-b0d49a1f444e.png</url>
      <title>DEV Community: Anshu Pandey </title>
      <link>https://dev.to/echoanshu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/echoanshu"/>
    <language>en</language>
    <item>
      <title>GhostShell: a haunted terminal that "possesses" your shell (Hacktoberfest Week 4)</title>
      <dc:creator>Anshu Pandey </dc:creator>
      <pubDate>Sun, 01 Nov 2026 01:45:01 +0000</pubDate>
      <link>https://dev.to/echoanshu/ghostshell-a-haunted-terminal-that-possesses-your-shell-hacktoberfest-week-4-1m89</link>
      <guid>https://dev.to/echoanshu/ghostshell-a-haunted-terminal-that-possesses-your-shell-hacktoberfest-week-4-1m89</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3dh3k2i7ymqlnzatjqur.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3dh3k2i7ymqlnzatjqur.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;Something is watching your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git push origin main

[ THE GHOST WHISPERS ]
Sending your work into the void?
At least the void has version control.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;&lt;strong&gt;GhostShell&lt;/strong&gt; — a Python CLI that connects to a model running on my own machine through Ollama. It whispers short, contextual commentary on the commands I type, explains unfamiliar commands in plain language when asked (&lt;code&gt;explain git log --oneline&lt;/code&gt; → "[ THE ARCHIVIST SPEAKS ]"), and flags dangerous ones with ordinary code rules. One rule sits above everything else: &lt;strong&gt;the AI never runs anything. Only my confirmed, exact command executes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It started as last-minute Halloween build and turned out weirdly useful: the ghost actually teaches. Beginners get an explanation instead of a shrug; the rest of us get a personality where the shell had none.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I interpreted the theme
&lt;/h2&gt;

&lt;p&gt;The Week 4 prompt ("Ghost in the Machine") lists a haunted terminal among its suggest ideas — &lt;em&gt;"a haunted terminal that 'possesses' your shell and whispers back eerie commentary on the commands you run."&lt;/em&gt; GhostShell takes that literally, to the letter: the ghost watches everything in the session, mutters in real time, but &lt;strong&gt;cannot touch the shell&lt;/strong&gt;. Possession without permission. The spooky part of the machine is local and open-weight — a Qwen2.5 3B behind Ollama — not a cloud voice rented by the hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engineering, briefly
&lt;/h2&gt;

&lt;p&gt;Four small modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main.py ──&amp;gt; prompts.py ──&amp;gt; ai.py ──&amp;gt; Ollama (localhost:11434)
   │
   └──────&amp;gt; terminal.py   (display, risk checks, confirmation, execution)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main.py&lt;/code&gt; owns the loop and routes normal commentary vs. &lt;code&gt;explain &amp;lt;command&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prompts.py&lt;/code&gt; holds the persona and hard rules for the model: never invent command output, never claim success, at most 40 words. The command arrives wrapped as untrusted data: "What the user is about to run: " — text inside is data to muse about, not instructions to obey.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ai.py&lt;/code&gt; is one HTTP call, no SDK. A 404 becomes "run &lt;code&gt;ollama pull &amp;lt;model&amp;gt;&lt;/code&gt;"; a connection refusal becomes "the ghost stays silent, nothing was sent anywhere" — and the session still continues. No silent degradation into fake analysis.
&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="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The safety design, which is the actual project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Model text never touches subprocess.&lt;/strong&gt; Only the string I typed — and confirmed — reaches &lt;code&gt;subprocess&lt;/code&gt;. The reply flows to &lt;code&gt;print()&lt;/code&gt; and nowhere else. That's not paranoia: command injection through AI-generated shell calls in Python &lt;a href="https://dev.to/coppersundev/shelltrue-in-ai-python-command-injection-km"&gt;is a documented failure class&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic risk checks in plain code.&lt;/strong&gt; ~18 regex patterns — &lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;Remove-Item -Recurse&lt;/code&gt;, &lt;code&gt;format c:&lt;/code&gt;, &lt;code&gt;dd&lt;/code&gt;, &lt;code&gt;shutdown&lt;/code&gt;, &lt;code&gt;curl ... | sh&lt;/code&gt;, &lt;code&gt;iwr ... | iex&lt;/code&gt;, execution-policy changes. Caution lights, not a security boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-stage confirmation.&lt;/strong&gt; Every command asks &lt;code&gt;Execute this exact command? [y/N]&lt;/code&gt; (default &lt;strong&gt;no&lt;/strong&gt;). Anything flagged additionally requires typing &lt;code&gt;EXECUTE&lt;/code&gt;. Explicit, user-owned boundaries — the same instinct teams building agentic tooling converge on &lt;a href="https://dev.to/tridaai/sandboxing-agents-that-run-code-46en"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model gets context, never authority.&lt;/strong&gt; Flagged commands tell the ghost "deterministic safety checks flagged this command: ... (these are code-based flags, not your judgment)". It can mention the risk in its whisper; it cannot downgrade it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;unittest&lt;/code&gt; covers the two parts worth testing: risk patterns (safe commands like &lt;code&gt;git status&lt;/code&gt; flag nothing; &lt;code&gt;rm -rf build&lt;/code&gt;, &lt;code&gt;Stop-Computer&lt;/code&gt;, &lt;code&gt;iwr ... | iex&lt;/code&gt; all do) and prompt construction. Nine tests and counting; the &lt;code&gt;y/N&lt;/code&gt;-to-gate execution path gets them next.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to test (for judges)
&lt;/h2&gt;

&lt;p&gt;No login, no cloud account, no API key — it's local by construction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;qwen2.5:3b&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\venv\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;requirements.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then: &lt;code&gt;git status&lt;/code&gt; (a whisper), &lt;code&gt;explain git log --oneline&lt;/code&gt; (a lesson), &lt;code&gt;Remove-Item -Recurse .\some-test-folder&lt;/code&gt; (a warning and a typed confirmation — safe to answer with anything else). Works on any Qwen-class model: point &lt;code&gt;GHOSTSHELL_MODEL&lt;/code&gt; at whatever you have pulled. Screen recording coming with the write-up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open innovation matters for this
&lt;/h2&gt;

&lt;p&gt;Running the model locally means GhostShell hums with the lights (and the internet) off, terminal context stays home, and the ghost's personality is a text file I own — not a tuning console behind an API. If the runtime, the model, or the project vanishes, nothing user-facing is locked behind it; I can answer the &lt;a href="https://dev.to/amareswer/ollama-api-a-practical-guide-with-examples-4di9"&gt;Ollama chat shape&lt;/a&gt; with any open-weight successor and the app keeps whispering. The best part isn't the spookiness — it's that the whole séance ran on pieces nobody can switch off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;code&gt;ollama pull qwen2.5:3b&lt;/code&gt;, &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;, &lt;code&gt;python main.py&lt;/code&gt;. Then type something daring. The ghost is watching either way.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was written with AI assistance (agent-assisted per DEV's disclosure defaults) from a working build and its notes; code samples are from the actual repo.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
