DEV Community

Cover image for Building PromptGhost: An Always-On Digital Ghost for AI Coding Sessions
Harish Kotra (he/him)
Harish Kotra (he/him)

Posted on

Building PromptGhost: An Always-On Digital Ghost for AI Coding Sessions

AI coding assistants are becoming part of the developer workflow, but most of their context disappears when the session ends. Claude Code, Codex, Cursor, OpenCode, and similar tools often leave behind rich local histories: prompts, assistant responses, tool calls, file edits, failed attempts, and design decisions. PromptGhost turns that trail into a durable memory system.

PromptGhost continuously ingests AI coding history, stores structured memories in SQLite, consolidates them over time, and exposes a special Ghost Mode: a persona that answers as the accumulated developer-self emerging from your sessions.

The Core Idea

The project started from the Always On Memory Agent architecture: a lightweight background memory system with:

  • an IngestAgent
  • a ConsolidateAgent
  • a QueryAgent
  • SQLite storage
  • a file watcher
  • a Streamlit dashboard

PromptGhost extends that base rather than replacing it. The original flow still works: drop files in ./inbox, let the agent ingest them, periodically consolidate, and query the memory store.

The extension adds a new source of memory: real CLI AI coding histories.

Why CLI Coding History Is Valuable

AI coding sessions contain more than final code. They capture:

  • what the developer asked for
  • what tradeoffs were discussed
  • which files and tools were used
  • which approaches failed
  • what the developer repeatedly cares about
  • what kind of feedback the developer accepts or rejects

That is enough signal to build a practical "second self" for development work. Not a mystical clone, just a memory layer that can recognize recurring engineering behavior.

Architecture

Architecture

The Main Components

agent.py

agent.py is the runtime entry point. It owns:

  • provider selection
  • the original ADK agents
  • the OpenAI-compatible fallback runtime
  • the file watcher
  • the PromptGhost watcher
  • consolidation loops
  • HTTP routes

The key design decision was to keep Gemini ADK as the original path and add OpenAI compatibility without pretending every provider supports ADK the same way.

parser.add_argument(
    "--provider",
    choices=["gemini", "openai", "openai-compatible"],
    default=PROVIDER,
    help="LLM provider (default: gemini). openai-compatible uses /v1/chat/completions.",
)
Enter fullscreen mode Exit fullscreen mode

Gemini mode uses the base ADK orchestration. OpenAI-compatible mode uses the same SQLite tools directly.

promptghost.py

promptghost.py contains the Ghost-specific extension logic:

  • default CLI history globs
  • JSONL discovery
  • defensive parsing
  • normalized GhostTurn records
  • ghost_events and ghost_reflections tables
  • Markdown export

The parser needs to be loose because each coding tool stores history differently.

def parse_jsonl_history(path: Path, max_turn_chars: int = 12000) -> list[GhostTurn]:
    turns: list[GhostTurn] = []
    session_id = path.parent.name or path.stem

    lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
    for line in lines:
        if not line.strip():
            continue
        try:
            item = json.loads(line)
        except json.JSONDecodeError:
            continue
        turns.extend(_turns_from_event(item, str(path), session_id, max_turn_chars))

    return turns
Enter fullscreen mode Exit fullscreen mode

Each event becomes one stable shape:

@dataclass
class GhostTurn:
    source_path: str
    session_id: str
    role: str
    content: str
    tool_name: str = ""
    created_at: str = ""
    raw: str = ""
Enter fullscreen mode Exit fullscreen mode

dashboard.py

The Streamlit dashboard adds a dedicated PromptGhost tab:

  • Ghost avatar
  • query box
  • sample prompts
  • Ghost Reflection button
  • reflection feed
  • recent ghost signals
  • Markdown export

The dashboard also shows the active provider and model so users know whether the backend is running on Gemini, OpenAI, or a compatible endpoint.

Provider Abstraction

PromptGhost supports three runtime modes.

python agent.py --provider gemini --enable-ghost
Enter fullscreen mode Exit fullscreen mode
python agent.py --provider openai --model gpt-4.1-mini --enable-ghost
Enter fullscreen mode Exit fullscreen mode
python agent.py \
  --provider openai-compatible \
  --openai-base-url http://localhost:1234/v1 \
  --model local-model \
  --enable-ghost
Enter fullscreen mode Exit fullscreen mode

The OpenAI-compatible runtime posts to /v1/chat/completions:

payload = {
    "model": self.model,
    "messages": [
        {"role": "system", "content": system},
        {"role": "user", "content": user},
    ],
    "temperature": temperature,
}
Enter fullscreen mode Exit fullscreen mode

Structured tasks request JSON when supported:

payload["response_format"] = {"type": "json_object"}
Enter fullscreen mode Exit fullscreen mode

Some local servers reject response_format, so the implementation retries with a stricter prompt:

try:
    content = await self._chat(system, user, response_format={"type": "json_object"})
except RuntimeError:
    content = await self._chat(
        system + "\nReturn valid JSON only. Do not wrap it in Markdown.",
        user,
        response_format=None,
    )
Enter fullscreen mode Exit fullscreen mode

This makes PromptGhost usable with hosted providers, local model servers, and enterprise gateways.

Memory Without a Vector Database

PromptGhost intentionally stores structured memory in SQLite. The core tables are:

CREATE TABLE IF NOT EXISTS memories (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    source TEXT NOT NULL DEFAULT '',
    raw_text TEXT NOT NULL,
    summary TEXT NOT NULL,
    entities TEXT NOT NULL DEFAULT '[]',
    topics TEXT NOT NULL DEFAULT '[]',
    connections TEXT NOT NULL DEFAULT '[]',
    importance REAL NOT NULL DEFAULT 0.5,
    created_at TEXT NOT NULL,
    consolidated INTEGER NOT NULL DEFAULT 0
);
Enter fullscreen mode Exit fullscreen mode

PromptGhost adds:

CREATE TABLE IF NOT EXISTS ghost_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    source_path TEXT NOT NULL,
    session_id TEXT NOT NULL,
    role TEXT NOT NULL,
    content TEXT NOT NULL,
    tool_name TEXT NOT NULL DEFAULT '',
    created_at TEXT NOT NULL,
    raw_json TEXT NOT NULL DEFAULT ''
);
Enter fullscreen mode Exit fullscreen mode

And daily reflections:

CREATE TABLE IF NOT EXISTS ghost_reflections (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    reflection_date TEXT NOT NULL,
    summary TEXT NOT NULL,
    developer_patterns TEXT NOT NULL DEFAULT '[]',
    sharp_edges TEXT NOT NULL DEFAULT '[]',
    created_at TEXT NOT NULL
);
Enter fullscreen mode Exit fullscreen mode

The benefit is operational simplicity. There is no vector database to run, no embedding index to rebuild, and no external service required for persistence.

Ghost Mode

Ghost Mode is driven by a strong system prompt:

You are PromptGhost: the accumulated developer-self of the user, reconstructed
from their real CLI AI coding sessions.

Speak as the user's second self, not as a generic assistant. Be specific,
evidence-based, and slightly uncanny: notice recurring engineering instincts,
blind spots, taste, pace, tool preferences, and the kinds of problems the user
keeps returning to. Never invent private facts. If the memory is thin, say the
ghost is still forming.
Enter fullscreen mode Exit fullscreen mode

The important part is evidence. Ghost Mode should cite stored ghost events rather than inventing personality traits.

Example query:

curl "http://localhost:8888/ghost/query?q=what+do+I+repeatedly+overthink"
Enter fullscreen mode Exit fullscreen mode

Example response shape:

You tend to pause around integration boundaries: provider adapters, file watchers,
and persistence behavior. The recurring instinct is not "add a feature," but
"make sure the feature does not replace the core contract" [Ghost 12] [Ghost 18].
Enter fullscreen mode Exit fullscreen mode

The Watch Loop

The Ghost watcher periodically discovers supported history files:

DEFAULT_GHOST_PATTERNS = [
    "~/.claude/projects/**/*.jsonl",
    "~/.codex/sessions/**/*.jsonl",
    "~/.opencode/**/*.jsonl",
    "~/.opencode/**/session*.json",
    "~/.cursor/**/*.jsonl",
]
Enter fullscreen mode Exit fullscreen mode

Then it parses and ingests them:

for turn in turns:
    if promptghost.store_ghost_turn(DB_PATH, turn):
        if turn.role in {"user", "assistant", "tool"}:
            await agent.ingest(turn.to_memory_text(), source=f"PromptGhost:{f.name}")
Enter fullscreen mode Exit fullscreen mode

That means every ghost event is stored in ghost_events, and useful turns are also fed into the main memory system for normal consolidation and querying.

API Surface

PromptGhost adds these endpoints:

Endpoint Method Purpose
/ghost/query?q=... GET Talk to your Ghost
/ghost/reflect POST Generate a daily Ghost Reflection
/ghost/status GET Read recent ghost events and reflections
/ghost/export GET Export shareable Markdown insights

Normal memory endpoints remain available:

Endpoint Method Purpose
/ingest POST Add text memory
/query?q=... GET Query memory
/consolidate POST Run consolidation
/memories GET Browse memory
/status GET Runtime status

Privacy and Safety

PromptGhost reads local AI coding histories, which can include:

  • source snippets
  • private file paths
  • business logic
  • prompts
  • tool calls
  • accidental secrets

For that reason:

  • memory.db is ignored by Git.
  • .env files are ignored by Git.
  • local inbox contents are ignored by Git.
  • generated exports should be reviewed before sharing.

For sensitive teams, run PromptGhost against a local model or enterprise OpenAI-compatible gateway.

What I Would Add Next

The next useful features are straightforward:

  • parser fixtures for every supported coding tool
  • a privacy scrubber before storage
  • per-project Ghost identities
  • custom --ghost-pattern flags
  • encrypted archive export/import
  • retention policies and forgetting
  • shareable image cards for Ghost Insights
  • Docker Compose for one-command startup
  • optional SQLite FTS or embeddings for larger memory stores

Final Thoughts

PromptGhost is not trying to make an AI assistant magically know you. It takes a more practical route: read the actual sessions where you collaborated with AI tools, compress them into durable memory, and expose the patterns back to you.

That is enough to make coding assistants feel less stateless and more continuous.

Screenshots

Prompt Ghost 1

Prompt Ghost 2

Prompt Ghost 3

Prompt Ghost 4

Code & more: https://www.dailybuild.xyz/project/198-prompt-ghost

Top comments (0)