Week 5 of 16

Build: Restructure Your Prompt Vault

Take your Week 3–4 code and organize it into a professional multi-file project. Same logic, cleaner structure, ready for the Flask app in Week 6.

Day 23 60 minutes Build

Day 23 of 80

What You're Building

Right now your vault lives in one file — probably prompt_vault.py or similar. Today you split it into a proper project structure that professional Python developers use on day one of any new project.

Target structure — prompt-vault/
prompt-vault/
  .env                ← your ANTHROPIC_API_KEY (never commit this)
  requirements.txt    ← list of packages to install
  prompts.json        ← your saved prompts
  helpers.py          ← reusable functions (load, save, validate)
  vault.py            ← the main script you run
Why This Structure? Separation of Concerns

helpers.py handles one thing: data. It knows how to read and write your JSON files, and how to validate user input. It doesn't care about menus or API calls.

vault.py handles one thing: interaction. It shows menus, talks to Claude, and calls helpers.py functions when it needs to touch data.

This matters because the Flask web app you'll build in Week 6 can also import helpers.py — no copy-pasting needed. The same functions that power your command-line tool will power your web interface. That's the leverage you get from organizing code properly.

Step 1: Create requirements.txt

Create a file called requirements.txt in your prompt-vault/ folder with these two lines:

requirements.txt Text
anthropic
python-dotenv

This file is a contract: it tells anyone (including you, on a new machine) exactly what to install. Instead of remembering to run pip install anthropic and pip install python-dotenv separately, they run one command: pip install -r requirements.txt.

You can pin versions later (anthropic==0.25.0) when you need to guarantee a project doesn't break after an update. For now, unpinned is fine.

Step 2: Create helpers.py

This is the heart of today's build. Create helpers.py and add the following. Every function here came from your Week 3–4 code — you're just moving it into its own home.

helpers.py Python
import json

PROMPTS_FILE = "prompts.json"
VALID_PLATFORMS = ["Kling", "Runway", "Veo"]


def load_prompts():
    """Load prompts from the JSON file. Returns empty list if file doesn't exist."""
    try:
        with open(PROMPTS_FILE, "r") as f:
            return json.load(f)
    except FileNotFoundError:
        return []


def save_prompts(prompts):
    """Save the prompts list to the JSON file."""
    with open(PROMPTS_FILE, "w") as f:
        json.dump(prompts, f, indent=2)


def validate_platform(platform):
    """Return the properly-cased platform name, or None if invalid."""
    cleaned = platform.strip().capitalize()
    if cleaned in VALID_PLATFORMS:
        return cleaned
    return None

Line 1: import json — this module handles all JSON reading and writing. It's part of Python's standard library, so no installation needed.

Lines 3–4: Module-level constants in ALL_CAPS. These are values shared across the whole file. Defining them once at the top means changing "prompts.json" later only requires editing one line.

Lines 7–12: load_prompts() wraps the file-open in a try/except. If the file doesn't exist yet (first run), it returns an empty list instead of crashing. Clean, defensive code.

Lines 15–18: save_prompts(prompts) takes the current list and writes it. indent=2 makes the JSON human-readable when you open the file.

Lines 21–25: validate_platform() strips whitespace and capitalizes the first letter, then checks against the valid list. Returns the cleaned name if valid, None if not. The caller handles the None case.

Step 3: Create vault.py

This is the main script — the one you'll run. It imports from helpers.py and handles the user-facing parts: menus, Claude API calls, and flow control.

vault.py Python
import os
import anthropic
from dotenv import load_dotenv
from helpers import load_prompts, save_prompts, validate_platform

load_dotenv()
client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))


def show_prompts():
    """Display all saved prompts."""
    prompts = load_prompts()
    if not prompts:
        print("\n  No prompts saved yet.\n")
        return
    print(f"\n--- Saved Prompts ({len(prompts)}) ---\n")
    for i, p in enumerate(prompts, 1):
        print(f"  {i}. [{p['platform']}] {p['shot']}")
        print(f"     {p['prompt'][:80]}...\n")


def generate_prompt():
    """Ask Claude to generate a shot prompt and optionally save it."""
    shot = input("\n  Describe your shot: ").strip()
    platform_raw = input("  Platform (Kling / Runway / Veo): ")
    platform = validate_platform(platform_raw)

    if not platform:
        print(f"  '{platform_raw}' isn't a valid platform. Try Kling, Runway, or Veo.\n")
        return

    print("\n  Generating...\n")
    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=512,
        messages=[{
            "role": "user",
            "content": f"Write a cinematic {platform} video prompt for: {shot}"
        }]
    )
    prompt_text = message.content[0].text
    print(f"  {prompt_text}\n")

    save_it = input("  Save this prompt? (y/n): ").strip().lower()
    if save_it == "y":
        prompts = load_prompts()
        prompts.append({"shot": shot, "platform": platform, "prompt": prompt_text})
        save_prompts(prompts)
        print("  Saved.\n")


def main():
    print("\n=== DVP Prompt Vault ===\n")
    while True:
        print("  1. View saved prompts")
        print("  2. Generate new prompt")
        print("  3. Quit")
        choice = input("\n  Choose: ").strip()
        if choice == "1":
            show_prompts()
        elif choice == "2":
            generate_prompt()
        elif choice == "3":
            print("  Bye.\n")
            break
        else:
            print("  Type 1, 2, or 3.\n")


if __name__ == "__main__":
    main()

Lines 1–4: The import block. Notice from helpers import ... on line 4 — this is the payoff for creating helpers.py. Python finds it in the same folder automatically.

Lines 6–7: Load the .env file and create the Anthropic client. These live at the top of the file, outside any function, because they're needed by the whole script.

Lines 10–18: show_prompts() calls load_prompts() (from helpers) to get the data, then formats and prints it. No file handling code here — helpers handles that.

Lines 21–45: generate_prompt() handles user input, calls validate_platform() (from helpers), calls Claude, and if the user confirms, calls save_prompts() (from helpers) to persist it.

Lines 48–63: main() is the entry point — the menu loop. It calls the other functions; it doesn't do logic itself.

Line 66–67: if __name__ == "__main__": ensures main() only runs when you execute this file directly. If another script imports vault.py, it won't trigger the menu loop. This is standard Python structure.

Step 4: Set Up and Run

Terminal — prompt-vault/
$ python -m venv venv
$ venv\Scripts\activate
(venv) $ pip install -r requirements.txt
Successfully installed anthropic-... python-dotenv-...

(venv) $ python vault.py

=== DVP Prompt Vault ===

  1. View saved prompts
  2. Generate new prompt
  3. Quit

  Choose:
Notice How Much Cleaner This Is

Open vault.py and read it top to bottom. Even without comments, you can follow it: import tools, set up the client, define three functions, run main. Compare this to your original single-file script — the logic is identical, but the organization tells a story. Anyone who opens this project for the first time knows immediately where the data logic lives (helpers.py) and where the interaction logic lives (vault.py). That clarity is worth the extra file.

End of Day Checklist

Tomorrow — Day 24: Build — Add History Logging

Tomorrow you add a new feature to the project you just restructured: every prompt generation gets logged to a history.json file with a timestamp. You'll use Python's datetime module and add two new functions to helpers.py — without changing a single line of the code that already works.