<?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: Harshit Rai</title>
    <description>The latest articles on DEV Community by Harshit Rai (@harshit_rai_14102005).</description>
    <link>https://dev.to/harshit_rai_14102005</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%2F4187226%2F52c85fb7-9633-4a61-bd90-d7c5c6583e95.png</url>
      <title>DEV Community: Harshit Rai</title>
      <link>https://dev.to/harshit_rai_14102005</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harshit_rai_14102005"/>
    <language>en</language>
    <item>
      <title>Okuribi — A Ghost in the Machine</title>
      <dc:creator>Harshit Rai</dc:creator>
      <pubDate>Sat, 31 Oct 2026 17:34:12 +0000</pubDate>
      <link>https://dev.to/harshit_rai_14102005/okuribi-a-ghost-in-the-machine-4fln</link>
      <guid>https://dev.to/harshit_rai_14102005/okuribi-a-ghost-in-the-machine-4fln</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Okuribi is a terminal-based, story-driven investigation game where the past refuses to stay buried.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The player is caught in a repeating loop around a mysterious incident. They explore locations, question people, investigate documents, connect clues, and eventually attempt a ritual that can bring the truth to light. Each loop gives the player another chance to notice something they missed, while failed attempts and corruption make the investigation more unsettling.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Ghost in the Machine&lt;/em&gt; idea here is a haunting hidden inside a system: old records, repeated conversations, secrets people are reluctant to reveal, and traces that survive when the world resets. The player has to work out what happened instead of simply being told the answer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The plot of this game is taken from the anime series: &lt;a href="https://www.imdb.com/title/tt2176165/" rel="noopener noreferrer"&gt;Another (2012)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this submission, I focused on getting the core gameplay and agent workflow in place first. &lt;strong&gt;The current interface is terminal-based.&lt;/strong&gt; I am prioritizing reliable game mechanics, NPC interactions, and archive retrieval before adding a UI.&lt;/p&gt;

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

&lt;p&gt;The current version is designed to run locally in the terminal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=QesZtGG7grA" rel="noopener noreferrer"&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%2Fpwspumoymtnacvpwo920.jpg" alt="Watch the video" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Suggested demo path:&lt;/strong&gt; start the game, talk to an NPC, ask two questions in the same conversation, type &lt;code&gt;/back&lt;/code&gt;, then perform another game action.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/kira14102005/okuribi" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;World Bible:&lt;/strong&gt; The canonical source for the premise, NPC profiles, clues, documents, game rules, and ending.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key implementation files:&lt;/strong&gt; &lt;code&gt;workflow.py&lt;/code&gt;, &lt;code&gt;game/npc_subgraph.py&lt;/code&gt;, &lt;code&gt;game/engine_main.py&lt;/code&gt;, &lt;code&gt;game/llm_context.py&lt;/code&gt;, and the World Bible.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I wanted the LLM to make the game feel alive without letting it decide the actual game rules. So I separated deterministic game mechanics from the language-model parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current architecture
&lt;/h3&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    PLAYER["Player&amp;lt;br/&amp;gt;Terminal input"] --&amp;gt; WORKFLOW

    subgraph ORCHESTRATION["1. LangGraph Orchestration"]
        WORKFLOW["Top-level Okuribi workflow"]
        BEGIN["Begin turn&amp;lt;br/&amp;gt;Reset turn-local outputs"]
        ACTIVE{"NPC conversation active?"}
        WORKFLOW --&amp;gt; BEGIN --&amp;gt; ACTIVE

        ACTIVE --&amp;gt;|No| PLANNER["Planner node&amp;lt;br/&amp;gt;Gemini + structured ActionPlan"]
        PLANNER --&amp;gt; CLARIFY{"Plan needs clarification?"}
        CLARIFY --&amp;gt;|Yes| ASK["Return clarification prompt"]
        ASK --&amp;gt; PLAYER
        CLARIFY --&amp;gt;|No| PLAN["Normalize plan into action request"]

        ACTIVE --&amp;gt;|Yes| NPC_INPUT["Read input as active NPC question"]
        NPC_INPUT --&amp;gt; EXIT_CHECK{"Input is /back or exit command?"}
        EXIT_CHECK --&amp;gt;|Yes| CLEAR["Clear active_npc_id"]
        CLEAR --&amp;gt; PLAYER
        EXIT_CHECK --&amp;gt;|No| NPC_REQUEST["Build question_npc request&amp;lt;br/&amp;gt;for the active NPC"]
    end

    PLAN --&amp;gt; ACTION_REQUEST
    NPC_REQUEST --&amp;gt; ACTION_REQUEST

    subgraph ACTION_LAYER["2. ActionType Separation and Typed Actions"]
        ACTION_REQUEST["ActionRequest&amp;lt;br/&amp;gt;Pydantic TypeAdapter validation"]
        ACTION_TYPE{"Discriminate by action_type"}
        ACTION_REQUEST --&amp;gt; ACTION_TYPE

        ACTION_TYPE --&amp;gt; MOVE["MoveLocationAction"]
        ACTION_TYPE --&amp;gt; INSPECT["InspectLocationAction"]
        ACTION_TYPE --&amp;gt; TALK["TalkNPCAction"]
        ACTION_TYPE --&amp;gt; SEARCH["SearchDocAction"]
        ACTION_TYPE --&amp;gt; CROSS["CrossCheckAction"]
        ACTION_TYPE --&amp;gt; RITUAL["PerformRitualAction"]
    end

    subgraph ENGINE["3. Deterministic Game Engine"]
        VALIDATE["Common action validation&amp;lt;br/&amp;gt;and action-specific validation"]
        AP["Calculate and deduct AP cost"]
        DISPATCH{"Dispatch by typed action"}
        EXECUTE["Execute matching action handler"]
        RESULT["Authoritative ActionResult&amp;lt;br/&amp;gt;and candidate GameState"]
        COMMIT["Return updated state on success&amp;lt;br/&amp;gt;Preserve original state on failure"]
        LOOP_CHECK{"AP reaches zero&amp;lt;br/&amp;gt;and game not already over?"}
        CALLING["Trigger The Calling&amp;lt;br/&amp;gt;Update corruption and ending state"]

        VALIDATE --&amp;gt; AP --&amp;gt; DISPATCH --&amp;gt; EXECUTE --&amp;gt; RESULT --&amp;gt; COMMIT
        COMMIT --&amp;gt; LOOP_CHECK
        LOOP_CHECK --&amp;gt;|Yes| CALLING
        LOOP_CHECK --&amp;gt;|No| ROUTE_RESULT
        CALLING --&amp;gt; ROUTE_RESULT
    end

    MOVE --&amp;gt; VALIDATE
    INSPECT --&amp;gt; VALIDATE
    TALK --&amp;gt; VALIDATE
    SEARCH --&amp;gt; VALIDATE
    CROSS --&amp;gt; VALIDATE
    RITUAL --&amp;gt; VALIDATE

    subgraph NPC_SYSTEM["4. NPC Conversation Subgraphs"]
        NPC_ROUTE{"Successful question_npc&amp;lt;br/&amp;gt;and NPC selected?"}
        ENTER["Enter conversation&amp;lt;br/&amp;gt;Set active_npc_id"]
        SELECT["Select subgraph using NPC ID"]

        subgraph NPC_SAE["NPC_SAE Subgraph — example"]
            SAE_CONTEXT["Build constrained context&amp;lt;br/&amp;gt;Persona + allowed facts + revealed clues"]
            SAE_LLM["Local Ollama&amp;lt;br/&amp;gt;Gemma 3 1B"]
            SAE_HISTORY["NPC-specific dialogue history&amp;lt;br/&amp;gt;NPC-scoped thread ID + MemorySaver"]
            SAE_CONTEXT --&amp;gt; SAE_LLM --&amp;gt; SAE_HISTORY
        end

        OTHER_NPC["Other NPC-specific subgraphs&amp;lt;br/&amp;gt;Own persona, context and history"]

        SELECT --&amp;gt; SAE_CONTEXT
        SELECT --&amp;gt; OTHER_NPC
    end

    ROUTE_RESULT{"Route by action result"}
    ROUTE_RESULT --&amp;gt;|Successful question_npc&amp;lt;br/&amp;gt;with no topic| ENTER
    ENTER --&amp;gt; PLAYER
    ROUTE_RESULT --&amp;gt;|Successful question_npc&amp;lt;br/&amp;gt;with topic| NPC_ROUTE
    NPC_ROUTE --&amp;gt; SELECT
    ROUTE_RESULT --&amp;gt;|Other action or failure| NARRATOR

    SAE_HISTORY --&amp;gt; NARRATOR
    OTHER_NPC --&amp;gt; NARRATOR

    subgraph ARCHIVE["5. Document RAG MCP — Archive Retrieval"]
        ENGINE_CLIENT["GameEngine archive_search callback&amp;lt;br/&amp;gt;search_docs_sync"]
        MCP_TOOL["Document RAG MCP server&amp;lt;br/&amp;gt;query_docs tool"]
        RETRIEVAL["Retrieve relevant archive documents&amp;lt;br/&amp;gt;and normalize results"]
        DOC_MAP["Canonical document_clue_map&amp;lt;br/&amp;gt;from World Bible / engine config"]
        EVIDENCE["Return document evidence&amp;lt;br/&amp;gt;and eligible clue IDs"]

        ENGINE_CLIENT --&amp;gt; MCP_TOOL --&amp;gt; RETRIEVAL
        RETRIEVAL --&amp;gt; EVIDENCE
        DOC_MAP --&amp;gt; EVIDENCE
    end

    EXECUTE -.-&amp;gt;|search_doc| ENGINE_CLIENT
    EXECUTE -.-&amp;gt;|cross_check may need archive evidence| ENGINE_CLIENT
    EVIDENCE --&amp;gt; RESULT

    subgraph CANONICAL["6. Canonical Game Data and State"]
        WORLD["World Bible / engine configuration&amp;lt;br/&amp;gt;Locations, NPCs, documents, rules"]
        STATE["GameState&amp;lt;br/&amp;gt;Location, AP, notebook, discovered clues,&amp;lt;br/&amp;gt;NPC state, corruption and phase"]
        RULES["Deterministic rule mappings&amp;lt;br/&amp;gt;Action costs, clue unlock rules,&amp;lt;br/&amp;gt;ritual solution and endings"]
    end

    WORLD --&amp;gt; VALIDATE
    WORLD --&amp;gt; EXECUTE
    RULES --&amp;gt; VALIDATE
    RULES --&amp;gt; EXECUTE
    STATE --&amp;gt; VALIDATE
    COMMIT --&amp;gt; STATE

    subgraph NARRATION["7. Player-Facing Response"]
        NARRATOR["Narrator node&amp;lt;br/&amp;gt;Gemini"]
        OUTPUT["Format event, result and permitted evidence"]
        TERMINAL["Display response in terminal"]
        NARRATOR --&amp;gt; OUTPUT --&amp;gt; TERMINAL --&amp;gt; PLAYER
    end

    COMMIT --&amp;gt; NARRATOR

    WORKFLOW -.-&amp;gt; CHECKPOINT["Top-level MemorySaver&amp;lt;br/&amp;gt;In-memory workflow checkpoint"]
    SAE_HISTORY -.-&amp;gt; NPC_CHECKPOINT["Separate in-memory NPC checkpointer&amp;lt;br/&amp;gt;Scoped by session and NPC ID"]

    classDef model fill:#e8e5ff,stroke:#7163a8,color:#17152b
    classDef engine fill:#dff3e4,stroke:#43815a,color:#102a19
    classDef retrieval fill:#fff0d9,stroke:#b47a29,color:#38250b
    classDef state fill:#e0efff,stroke:#4c7da8,color:#12263a

    class PLANNER,SAE_LLM,NARRATOR model
    class VALIDATE,AP,DISPATCH,EXECUTE,RESULT,COMMIT,LOOP_CHECK,CALLING engine
    class ENGINE_CLIENT,MCP_TOOL,RETRIEVAL,DOC_MAP,EVIDENCE retrieval
    class WORLD,STATE,RULES state&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The planner interprets a new request as one game action: moving, inspecting a location, talking to an NPC, searching a document, cross-checking documents, or attempting the ritual. The action is converted into a typed &lt;code&gt;ActionRequest&lt;/code&gt; and passed to the engine.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;GameEngine is the authority&lt;/strong&gt;. It validates actions, applies action-point costs, checks game conditions, and updates state. The LLM cannot directly unlock clues, change corruption, or decide whether a ritual succeeds.&lt;/p&gt;

&lt;p&gt;The World Bible provides the canonical story and game data. For archive investigation, the workflow reuses my existing MCP document-search tool instead of treating the LLM's generated text as evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Gemma?
&lt;/h3&gt;

&lt;p&gt;For NPC dialogue, I use &lt;strong&gt;Gemma3:1B locally through Ollama&lt;/strong&gt;. The planner and narrator currently use Gemini; Gemma is the model that plays the characters.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffqraopv6uc3s2s49aquv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffqraopv6uc3s2s49aquv.png" alt="Gemma3:1B is configured through Ollama " width="781" height="243"&gt;&lt;/a&gt;&lt;br&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%2F6wa0elkfer53hcwt2vlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6wa0elkfer53hcwt2vlv.png" alt="Local Gemma3 invoked by the NPC conversation subgraph" width="800" height="373"&gt;&lt;/a&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl97gsrf6ihsnwfag5wfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl97gsrf6ihsnwfag5wfm.png" alt="Gemma3:1B is available in my local Ollama environment" width="604" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I chose this setup so I could experiment with NPC behavior locally, control the prompts, and keep character dialogue separate from game mechanics. Each NPC gets its own persona and knowledge context, rather than loading a separate model for every character. The same local Gemma model can play different roles through different prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  NPC conversations and subgraphs
&lt;/h3&gt;

&lt;p&gt;Talking to an NPC is a two-stage interaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The player selects an NPC, for example &lt;code&gt;Talk to NPC_SAE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The workflow records the active NPC and asks the player what they want to ask.&lt;/li&gt;
&lt;li&gt;The next input is treated as a question for that NPC, not as a new global action.&lt;/li&gt;
&lt;li&gt;The engine validates the question action. If it succeeds, the workflow calls that NPC's subgraph.&lt;/li&gt;
&lt;li&gt;The NPC subgraph generates a response using Gemma and that character's recent conversation history. Typing &lt;code&gt;/back&lt;/code&gt; returns to the main game planner.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code creates a separate subgraph for each NPC ID in the World Bible, with a separate in-memory checkpointer and a thread ID scoped to the session and NPC. This keeps character conversation histories isolated from each other.&lt;/p&gt;

&lt;p&gt;The memory is currently &lt;strong&gt;in-memory, not durable across application restarts&lt;/strong&gt;. A SQLite or PostgreSQL checkpointer is a future improvement.&lt;/p&gt;

&lt;p&gt;NPCs also have knowledge boundaries and allowed clue IDs. The engine remains responsible for whether a clue is actually discovered; NPC dialogue does not mutate the game state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current implementation and next steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In the current code:&lt;/strong&gt; terminal interaction, typed action planning, deterministic action execution, NPC conversation routing, individual NPC subgraphs, in-memory per-NPC conversation history, World Bible-backed game data, and archive retrieval through MCP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next:&lt;/strong&gt; run end-to-end tests across all action types and NPCs, harden topic/clue unlocking, and improve fallback behavior for malformed model outputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI work:&lt;/strong&gt; add a Streamlit interface with a separate chat branch/panel for each NPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory work:&lt;/strong&gt; replace in-memory checkpointers with durable storage so NPC histories survive process restarts.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Open innovation makes this project practical to experiment with. I can run Gemma locally through Ollama, inspect and change prompts, define what each character is allowed to know, and keep the core game rules in code that I can test.&lt;/p&gt;

&lt;p&gt;I can also combine tools instead of locking the project into one provider: LangGraph coordinates the workflow, Pydantic validates actions, the game engine owns deterministic mechanics, MCP exposes archive retrieval, and Gemma generates NPC dialogue locally. I can improve one part without rewriting the whole game.&lt;/p&gt;

&lt;p&gt;For a mystery game, this separation matters. I want the model to create believable conversations, but I don't want the truth of the story to change just because a model generated something plausible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spirit of It
&lt;/h2&gt;

&lt;p&gt;The feeling I want to build toward is that the game remembers something the player almost forgot. A loop resets the immediate situation, but the investigation should not feel like starting from zero. Notes and discovered evidence help the player push back against the repetition, while NPCs can react through their own conversations.&lt;/p&gt;

&lt;p&gt;I am keeping the first version small on purpose: make the rules reliable, make the NPCs feel distinct, and make the mystery worth investigating. The current terminal version is the foundation; a richer UI and durable character memory can come next.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Best Use of Gemma
&lt;/h3&gt;

&lt;p&gt;I am submitting Okuribi for &lt;strong&gt;Best Use of Gemma&lt;/strong&gt;. Gemma 3 1B runs locally through Ollama and generates the dialogue for the game's NPCs. Each NPC has a distinct persona, dialogue style, and constrained knowledge context, and each NPC ID has its own conversation subgraph and isolated in-memory dialogue history. The deterministic game engine—not the model—controls action validation and game-state changes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is an MVP, and I am building it incrementally: first a reliable game loop and character interactions, then a richer interface and durable NPC memory.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>pgaichallenge</category>
    </item>
    <item>
      <title>GAI: Let AI Generate Your Git Commit Messages Locally</title>
      <dc:creator>Harshit Rai</dc:creator>
      <pubDate>Wed, 21 Oct 2026 20:57:31 +0000</pubDate>
      <link>https://dev.to/harshit_rai_14102005/gai-let-ai-generate-your-git-commit-messages-locally-4gb7</link>
      <guid>https://dev.to/harshit_rai_14102005/gai-let-ai-generate-your-git-commit-messages-locally-4gb7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Writing commit messages is one of those small tasks developers do constantly but rarely enjoy. After debugging an issue or implementing a feature, describing the changes clearly can feel like unnecessary friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GAI&lt;/strong&gt; is my attempt to automate that part of the development workflow without relying on external LLM APIs.&lt;/p&gt;

&lt;p&gt;GAI analyzes staged Git changes, retrieves similar commits from historical commit data, and uses a locally running language model to generate a commit message that follows the repository's existing tagging conventions.&lt;/p&gt;

&lt;p&gt;Instead of simply sending an entire Git diff to a model, GAI combines two approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval-augmented generation (RAG):&lt;/strong&gt; Finds historically similar changes using embeddings and FAISS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change-focused diff parsing:&lt;/strong&gt; Extracts file paths, hunks, additions, deletions, and relevant context to make the changes easier for the generation model to understand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use GAI in my own development workflow to reduce the friction of writing commit messages.&lt;/p&gt;

&lt;p&gt;The generated message is not committed automatically. I can accept it, edit it, regenerate it, or cancel the operation. Before committing, GAI also checks that the staged changes have not changed since the message was generated.&lt;/p&gt;

&lt;p&gt;The idea is simple: spend less time writing commit messages and more time writing code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A([Run gai]) --&amp;gt; AZ[Invokes src/cli.py]
    AZ --&amp;gt; B[Validate Git repository]
    B --&amp;gt; C[Read staged Git diff]
    C --&amp;gt; D{Staged changes exist?}

    D -- No --&amp;gt; E[Display error and exit]
    D -- Yes --&amp;gt; F[Preserve raw diff snapshot]

    F --&amp;gt; G[Parse diff by file and hunk]
    G --&amp;gt; H["Extract added lines, &amp;lt;br&amp;gt;removed lines and context"]
    H --&amp;gt; I["Format structured diff &amp;lt;br&amp;gt;for generation"]

    F --&amp;gt; J[Generate embedding &amp;lt;br&amp;gt;from raw diff]
    J --&amp;gt; K[Search FAISS index]
    K --&amp;gt; L["Retrieve top-k &amp;lt;br&amp;gt;historical commits"]

    I --&amp;gt; M[Build generation prompt]
    L --&amp;gt; M

    M --&amp;gt; N["Call local Gemma model &amp;lt;br&amp;gt;through Ollama"]
    N --&amp;gt; O[Parse generated commit message]
    O --&amp;gt; P{Message valid?}

    P -- No --&amp;gt; Q["Handle invalid output &amp;lt;br&amp;gt;or generation error"]
    P -- Yes --&amp;gt; R["Display suggested &amp;lt;br&amp;gt;commit message"]

    R --&amp;gt; S{User choice}
    S -- Edit --&amp;gt; T[Enter replacement message]
    T --&amp;gt; U{Edited message valid?}
    U -- Yes --&amp;gt; R
    U -- No --&amp;gt; R

    S -- Regenerate --&amp;gt; M
    S -- Cancel --&amp;gt; V([Exit without committing])

    S -- Accept --&amp;gt; W[Read current staged diff again]
    W --&amp;gt; X{Raw diff unchanged?}

    X -- No --&amp;gt; Y[Abort commit and request rerun]
    X -- Yes --&amp;gt; Z[Execute git commit]
    Z --&amp;gt; AA{Commit successful?}
    AA -- Yes --&amp;gt; AB([Done])
    AA -- No --&amp;gt; AC[Display Git error]&lt;/code&gt;&lt;/pre&gt;



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

&lt;p&gt;GAI is a local command-line tool, so it runs directly in a developer's environment.&lt;br&gt;
Here's how I use GAI on my own code changes.&lt;br&gt;
&lt;strong&gt;1. Stage the changes&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;git add README.md
git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--stat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcagrrks4bxp8g5y7pj4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcagrrks4bxp8g5y7pj4.png" alt="Staged git diff from VS code UI" width="799" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Generate a commit message&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;gai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq15u2c5w4jd3jr98g7xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq15u2c5w4jd3jr98g7xj.png" alt="gai terminal execution -- formatted-diff" width="800" height="602"&gt;&lt;/a&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flt0eie861vz5c5olu1ht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flt0eie861vz5c5olu1ht.png" alt="gai terminal execution -- generating the commit using gemma" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GAI retrieves similar historical commits, generates a message, and lets me review it before committing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Verify the commit (optional)&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;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;--oneline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2qoel16bl1hchvgfsa8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2qoel16bl1hchvgfsa8.png" alt="Git log output showing the commit created using GAI's generated message" width="798" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;GitHub repository:&lt;/strong&gt; &lt;a href="https://github.com/kira14102005/gai" rel="noopener noreferrer"&gt;https://github.com/kira14102005/gai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GAI is implemented in Python and can also be invoked through its Python CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can control the number of historical examples retrieved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.cli &lt;span class="nt"&gt;--top-k&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository README contains the setup instructions, FAISS index preparation steps, and details of the current workflow.&lt;/p&gt;

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

&lt;p&gt;The current implementation uses &lt;strong&gt;Ollama, FAISS, EmbeddingGemma, and Gemma4:e2b&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I started with historical commit data containing tags, messages, and diffs. The indexing pipeline generates embeddings for usable historical diffs using EmbeddingGemma and stores them in a FAISS &lt;code&gt;IndexFlatL2&lt;/code&gt; index. The corresponding commit metadata is stored separately so retrieved vectors can be mapped back to their original messages and tags.&lt;/p&gt;

&lt;p&gt;When I run GAI, it reads the staged Git diff. The raw diff is used for retrieval, while a parsed and formatted representation gives the generation model a clearer view of the actual changes.&lt;/p&gt;

&lt;p&gt;FAISS retrieves the most relevant historical commits, and their messages and tags are included alongside the formatted diff in the generation prompt. Gemma then generates a candidate message through Ollama.&lt;/p&gt;

&lt;p&gt;The CLI handles the final review. I remain in control of whether a commit is created, and GAI verifies that the staged diff is unchanged before committing.&lt;/p&gt;

&lt;p&gt;One challenge I'm interested in solving is how smaller local models handle large diffs. Simply increasing the context window isn't always the best solution; giving the model better-structured information may be more useful.&lt;/p&gt;

&lt;p&gt;My planned next step is a parallel LangGraph workflow with two generation paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A retrieval-based chain that uses historical commits, as the current implementation does.&lt;/li&gt;
&lt;li&gt;A diff-understanding chain that summarizes large changes by file and hunk before generating another candidate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An evaluator would assess both candidates for correctness, relevance, specificity, and tag validity. A reducer would then select the stronger candidate or synthesize a better one when necessary.&lt;/p&gt;

&lt;p&gt;These LangGraph chains, evaluator, and reducer are future improvements; they are not yet part of the current implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Innovation Matters
&lt;/h2&gt;

&lt;p&gt;I wanted GAI to generate commit messages that reflect how I actually write commits, rather than producing generic descriptions of code changes.&lt;/p&gt;

&lt;p&gt;That's why I combined &lt;strong&gt;local inference with retrieval from historical commits&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learning from existing conventions:&lt;/strong&gt; FAISS retrieves similar historical diffs and their commit messages, giving Gemma concrete examples of the repository's tagging conventions and writing style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeping code local:&lt;/strong&gt; Git diffs can contain proprietary implementation details. Running both embedding and generation models through Ollama lets GAI process these changes locally without depending on an external LLM API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experimenting without being locked in:&lt;/strong&gt; I can change the generation model, refine prompts, or experiment with retrieval strategies without redesigning the application around a particular hosted AI provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting part isn't simply running an LLM locally. It's combining open models with a repository's own history to build a commit-message generator tailored to the way its developers work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;The goal is to remove a repetitive task from the development loop.&lt;/p&gt;

&lt;p&gt;Instead of switching from coding to thinking about the wording of a commit message, I can stage my changes, run &lt;code&gt;gai&lt;/code&gt;, review the suggestion, and continue.&lt;/p&gt;

&lt;p&gt;The human review step is intentional. Automating the boring part shouldn't mean giving up control over the final commit.&lt;/p&gt;

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

&lt;p&gt;I'm planning to experiment with parallel generation chains, hierarchical diff summarization, and an evaluator/reducer workflow to improve the quality of generated messages.&lt;/p&gt;

&lt;p&gt;I'd love to hear how others approach commit-message generation, especially when working with smaller local models. What would you improve in this pipeline?&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — GAI uses &lt;em&gt;gemma4:e2b&lt;/em&gt; through Ollama to generate commit messages locally, combining open-weight AI with retrieval from historical Git commits.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
