Claude CLI Helper (Python Minimal Clone)

A quick‑start guide to build a local command‑line assistant using Claude‑style prompts

Welcome to another official UVF IT tutorial! In this guide we’ll craft a tiny CLI helper that mimics Claude’s prompt‑template workflow, perfect for experimenting without any paid APIs.

You’ll spin up a Python project, store templates in SQLite, and query them from the terminal – all in under 45 minutes.

Before you start

Minimal Claude CLI Helper Architecture
LOCAL RUNTIME EXTERNAL SERVICES CRUD fetch (optional) CLI Entrypoint (Python) SQLite DB (templates) Optional Remote Template Repo
1

Phase 1: Scaffold the project

First we set up an isolated Python environment so our dependencies don't clash with system packages. Then we install the tiny HTTP client (requests) that will let the CLI optionally pull templates from a remote repo. Keeping everything local makes the clone lightweight and easy to run on any machine.

python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install requests
2

Phase 2: Define the data model (SQLite)

We need a simple way to persist prompt templates. SQLite is perfect because it lives in a single file and requires no server. The helper will create a templates.db file on first run and expose functions to add, fetch, and list templates.

db.py

import sqlite3
from pathlib import Path

DB_PATH = Path(__file__).with_name('templates.db')

def _init_db():
    conn = sqlite3.connect(DB_PATH)
    cur = conn.cursor()
    cur.execute('''
        CREATE TABLE IF NOT EXISTS templates (
            id   INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL UNIQUE,
            body TEXT NOT NULL
        )
    ''')
    conn.commit()
    conn.close()

def get_conn():
    if not DB_PATH.exists():
        _init_db()
    return sqlite3.connect(DB_PATH)

def add_template(name: str, body: str):
    conn = get_conn()
    cur = conn.cursor()
    cur.execute('INSERT INTO templates (name, body) VALUES (?, ?)', (name, body))
    conn.commit()
    conn.close()

def get_template(name: str):
    conn = get_conn()
    cur = conn.cursor()
    cur.execute('SELECT body FROM templates WHERE name = ?', (name,))
    row = cur.fetchone()
    conn.close()
    return row[0] if row else None

def list_templates():
    conn = get_conn()
    cur = conn.cursor()
    cur.execute('SELECT name FROM templates')
    names = [r[0] for r in cur.fetchall()]
    conn.close()
    return names
3

Phase 3: Core CLI (argparse)

In this step we wire up a tiny command‑line interface that talks to the SQLite database you created earlier. We use Python's built‑in argparse module so the tool feels familiar and requires no extra dependencies. The CLI will understand three sub‑commands – add to store a new template, show to retrieve a single template by name, and list to display all stored names. Each command opens the same templates.db file, runs a small query, and prints a friendly result for the user.

main.py

import argparse, sqlite3, sys
DB_PATH = 'templates.db'

def get_conn():
    return sqlite3.connect(DB_PATH)

def cmd_add(args):
    conn = get_conn()
    cur = conn.cursor()
    cur.execute('INSERT INTO tmpl (name, content) VALUES (?, ?)', (args.name, args.content))
    conn.commit()
    print(f"✅ Added template '{args.name}'.")

def cmd_show(args):
    conn = get_conn()
    cur = conn.cursor()
    cur.execute('SELECT content FROM tmpl WHERE name = ?', (args.name,))
    row = cur.fetchone()
    if row:
        print(row[0])
    else:
        print(f"⚠️ No template named '{args.name}'.", file=sys.stderr)

def cmd_list(_):
    conn = get_conn()
    cur = conn.cursor()
    cur.execute('SELECT name FROM tmpl ORDER BY name')
    for (name,) in cur.fetchall():
        print(name)

def main():
    parser = argparse.ArgumentParser(prog='claude-cli', description='Minimal Claude‑style template manager')
    sub = parser.add_subparsers(dest='command', required=True)
    # add command
    p_add = sub.add_parser('add', help='Add a new template')
    p_add.add_argument('name', help='Template identifier')
    p_add.add_argument('content', help='Template body')
    p_add.set_defaults(func=cmd_add)
    # show command
    p_show = sub.add_parser('show', help='Show a template')
    p_show.add_argument('name', help='Template identifier')
    p_show.set_defaults(func=cmd_show)
    # list command
    p_list = sub.add_parser('list', help='List all templates')
    p_list.set_defaults(func=cmd_list)
    args = parser.parse_args()
    args.func(args)

if __name__ == '__main__':
    main()
4

Phase 4: Run locally and test

Now that the CLI is in place, activate the virtual environment you set up earlier and try the three commands. Adding a template can be done directly from the shell or via a pipe, then you can list all names and finally show the stored content. Because everything runs against a local SQLite file, there is no network traffic – perfect for quick iteration and debugging.

# Activate the virtual environment (if not already active)
source .venv/bin/activate

# Add a template called 'greeting' using a pipe (simulates stdin)
echo "Hello, {{user}}!" | python main.py add greeting "$(cat)"

# List all stored templates to verify the entry
python main.py list

# Retrieve and display the 'greeting' template
python main.py show greeting
5

Phase 5: Extend – optional remote fetch

For a more cloud‑like experience you can pull a JSON file that contains an array of template objects and load them into the same database. This tiny helper script fetches the remote file, iterates over each entry, and uses the same INSERT logic as the CLI. You can run it once to seed the DB or schedule it periodically if the remote source updates.

The JSON format expected is simple: each object must have a name string and a content string. If you host the file on GitHub Pages, a raw URL works out of the box. The script is deliberately lightweight – no external HTTP libraries, just the standard library's urllib.

fetch.py

import json, sqlite3, urllib.request, sys
DB_PATH = 'templates.db'
REMOTE_URL = 'https://example.com/templates.json'  # replace with your URL

def get_conn():
    return sqlite3.connect(DB_PATH)

def fetch_json(url):
    with urllib.request.urlopen(url) as resp:
        if resp.status != 200:
            sys.exit(f'❌ Failed to fetch {url}: HTTP {resp.status}')
        return json.load(resp)

def load_templates(data):
    conn = get_conn()
    cur = conn.cursor()
    for tmpl in data:
        name = tmpl.get('name')
        content = tmpl.get('content')
        if not name or not content:
            print(f'⚠️ Skipping invalid entry: {tmpl}', file=sys.stderr)
            continue
        cur.execute('INSERT OR REPLACE INTO tmpl (name, content) VALUES (?, ?)', (name, content))
    conn.commit()
    print(f'✅ Loaded {len(data)} templates into {DB_PATH}')

def main():
    data = fetch_json(REMOTE_URL)
    if not isinstance(data, list):
        sys.exit('❌ Expected a JSON array of templates')
    load_templates(data)

if __name__ == '__main__':
    main()

Verifying the Stack

CheckCommand / Why it matters
Virtual environment activatedsource .venv/bin/activate && python -c "import sys; print(sys.prefix)"
Database created and contains the added templatesqlite3 templates.db "SELECT name FROM tmpl;"
CLI prints the template textpython main.py show greeting

What’s next?

Try adding tags, search by keyword, or integrate a local LLM for real generation. The sky’s the limit!