Metadata-Version: 2.3
Name: promptfinder
Version: 0.1.0
Summary: Fuzzy and semantic finder for prompts from Codex, Claude Code and Cursor sessions
Author: Kristian Klemon
Requires-Dist: pydantic>=2.11
Requires-Dist: pydantic-ai-slim[google,openai]>=2.35
Requires-Dist: textual>=8.2
Requires-Dist: typer>=0.27
Requires-Dist: sqlite-vec>=0.1.9
Requires-Dist: watchdog>=6.0
Requires-Dist: tiktoken>=0.14
Requires-Dist: tomli-w>=1.2
Requires-Dist: pydantic-ai-slim[cohere] ; extra == 'cohere'
Requires-Python: >=3.12
Provides-Extra: cohere
Description-Content-Type: text/markdown

# promptfinder

Fuzzy and semantic finder for the prompts you typed into **Codex**, **Claude Code** and **Cursor** — and the answers you got back. One local SQLite index, a Textual TUI, cloud embeddings only when you turn them on.

```
uv tool install .          # or: uv tool install promptfinder (once published)
promptfinder               # opens the TUI; the first launch runs the onboarding wizard
```

## What it does

- Discovers coding-agent sessions on macOS and Linux: `~/.codex/sessions` (+ `archived_sessions`), `~/.claude/projects`, Cursor's `globalStorage/state.vscdb`.
- Indexes human prompts (inputs) and, optionally, the final model response of every turn (outputs). Tool calls, tool results, reasoning and system/meta messages are never indexed.
- Groups sessions by **project = git root** of the session's working directory; inside a repo you can flip between *current project* and *global*.
- **Fuzzy search** (fzf-style, no external binary) over message bodies and metadata; **semantic search** through Gemini Embedding 2 (default) or another provider, stored in `sqlite-vec`.
- Keeps indexed copies when a source disappears — they are shown with an *archived* badge and stay searchable.
- Re-indexes on startup and, optionally, through a background watcher installed as a launchd LaunchAgent / systemd user unit.

## Commands

```
promptfinder                       TUI (fuzzy by default; ctrl+y copy, ctrl+t semantic, ctrl+g scope, ctrl+a agents, ctrl+r roles)
promptfinder --select              print the chosen message on Enter (e.g. codex "$(promptfinder --select)")
promptfinder onboard               re-run the wizard

promptfinder index [--full] [--embed] [--yes] [--agent codex] [--json]
promptfinder search "landing page" [--mode fuzzy|semantic] [--current|--global] [--project PATH]
                                   [--agent codex,…] [--role input|output|both] [--since 2026-01-01]
                                   [--limit 10] [--no-archived] [--format text|json|bodies|full]
promptfinder copy "landing page"   copy the best hit to the clipboard (--pick 2 for the second, same filters as search)
promptfinder copy --id 1234        copy a message by id (ids come from search --format json)
promptfinder copy                  interactive picker: Enter copies the highlighted prompt and exits
promptfinder status [--json]       counts, embedding coverage, daemon state
promptfinder doctor [--online]     discovery, adapter sniff, sqlite-vec, keys, service, diagnostics
promptfinder config show|path|set KEY VALUE|unset KEY|edit
promptfinder purge --embeddings [--index NAME] | --all
promptfinder rebuild               re-index everything (cached vectors are re-used, nothing is re-embedded)
promptfinder daemon run|install|uninstall|status|logs
```

## Configuration

`~/.config/promptfinder/config.toml` (also on macOS; `$XDG_CONFIG_HOME` is honoured). API keys are stored in plaintext by design — the file is created with mode `0600`. Environment variables (`GOOGLE_API_KEY`/`GEMINI_API_KEY`, `OPENAI_API_KEY`, `OPENROUTER_API_KEY`) are used as a fallback for interactive use; the background daemon only sees the config file.

```toml
[indexing]
agents = ["codex", "claude_code", "cursor"]
roles = ["input"]                 # add "output" to index final responses

[search]
default_mode = "fuzzy"            # fuzzy | semantic
default_scope = "current"         # current | global (current applies only inside a git repo)

[embeddings]
enabled = true
active_index = "google-gemini-embedding-2-3072"
[[embeddings.indexes]]
name = "google-gemini-embedding-2-3072"
provider = "google"
model = "gemini-embedding-2"
dimensions = 3072

[providers.google]
api_key = "…"
```

Changing the embedding provider, model or dimensions creates a new index; the active one is selected with `embeddings.active_index`. Nothing is sent to a provider until the backfill has been confirmed once (`backfill_confirmed_at`), after which the watcher embeds new messages automatically.

Data lives in `~/.local/share/promptfinder/index.sqlite`; daemon logs in `~/.local/state/promptfinder/`.

## Development

```
uv sync --all-groups
uv run promptfinder doctor
uv run pytest
uv run ruff check src tests
```

See `docs/prompts/plan.md` for the design and the verified on-disk formats of the three agents.
