File size: 5,857 Bytes
77c25ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""SQLite database layer for Prompt Garage."""

import os
import sqlite3

DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "prompts.db")

SCHEMA = """
CREATE TABLE IF NOT EXISTS prompts (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    source TEXT,
    is_starred INTEGER DEFAULT 0,
    tags TEXT,
    model TEXT,
    rating INTEGER,
    use_count INTEGER DEFAULT 0,
    created_at TEXT DEFAULT (datetime('now')),
    updated_at TEXT DEFAULT (datetime('now'))
);

CREATE VIRTUAL TABLE IF NOT EXISTS prompts_fts USING fts5(
    title, content, tags, content=prompts, content_rowid=id
);

CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON prompts BEGIN
    INSERT INTO prompts_fts(rowid, title, content, tags)
    VALUES (new.id, new.title, new.content, new.tags);
END;

CREATE TRIGGER IF NOT EXISTS prompts_ad AFTER DELETE ON prompts BEGIN
    INSERT INTO prompts_fts(prompts_fts, rowid, title, content, tags)
    VALUES('delete', old.id, old.title, old.content, old.tags);
END;

CREATE TRIGGER IF NOT EXISTS prompts_au AFTER UPDATE ON prompts BEGIN
    INSERT INTO prompts_fts(prompts_fts, rowid, title, content, tags)
    VALUES('delete', old.id, old.title, old.content, old.tags);
    INSERT INTO prompts_fts(rowid, title, content, tags)
    VALUES (new.id, new.title, new.content, new.tags);
END;
"""


def get_db() -> sqlite3.Connection:
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    return conn


def init_db():
    conn = get_db()
    conn.executescript(SCHEMA)
    conn.close()


def add_prompt(title, content, source="manual", tags=None, model=None, rating=None, is_starred=0):
    conn = get_db()
    cur = conn.execute(
        "INSERT INTO prompts (title, content, source, tags, model, rating, is_starred) VALUES (?, ?, ?, ?, ?, ?, ?)",
        (title, content, source, tags, model, rating, is_starred),
    )
    conn.commit()
    pid = cur.lastrowid
    conn.close()
    return pid


def list_prompts(starred=None, tag=None, model=None, limit=20):
    conn = get_db()
    query = "SELECT * FROM prompts WHERE 1=1"
    params = []
    if starred:
        query += " AND is_starred = 1"
    if tag:
        query += " AND tags LIKE ?"
        params.append(f"%{tag}%")
    if model:
        query += " AND model = ?"
        params.append(model)
    query += " ORDER BY updated_at DESC LIMIT ?"
    params.append(limit)
    rows = conn.execute(query, params).fetchall()
    conn.close()
    return rows


def get_prompt(pid):
    conn = get_db()
    row = conn.execute("SELECT * FROM prompts WHERE id = ?", (pid,)).fetchone()
    conn.close()
    return row


def delete_prompt(pid):
    conn = get_db()
    conn.execute("DELETE FROM prompts WHERE id = ?", (pid,))
    conn.commit()
    conn.close()


def update_prompt(pid, **kwargs):
    conn = get_db()
    sets = ", ".join(f"{k} = ?" for k in kwargs)
    vals = list(kwargs.values()) + [pid]
    conn.execute(f"UPDATE prompts SET {sets}, updated_at = datetime('now') WHERE id = ?", vals)
    conn.commit()
    conn.close()


def toggle_star(pid):
    conn = get_db()
    conn.execute("UPDATE prompts SET is_starred = 1 - is_starred, updated_at = datetime('now') WHERE id = ?", (pid,))
    conn.commit()
    row = conn.execute("SELECT is_starred FROM prompts WHERE id = ?", (pid,)).fetchone()
    conn.close()
    return row["is_starred"] if row else None


def set_rating(pid, rating):
    update_prompt(pid, rating=rating)


def increment_use(pid):
    conn = get_db()
    conn.execute("UPDATE prompts SET use_count = use_count + 1, updated_at = datetime('now') WHERE id = ?", (pid,))
    conn.commit()
    conn.close()


def search_fts(query):
    conn = get_db()
    rows = conn.execute(
        "SELECT p.* FROM prompts p JOIN prompts_fts f ON p.id = f.rowid WHERE prompts_fts MATCH ? ORDER BY rank",
        (query,),
    ).fetchall()
    conn.close()
    return rows


def get_stats():
    conn = get_db()
    total = conn.execute("SELECT COUNT(*) as c FROM prompts").fetchone()["c"]
    starred = conn.execute("SELECT COUNT(*) as c FROM prompts WHERE is_starred = 1").fetchone()["c"]
    by_source = conn.execute("SELECT source, COUNT(*) as c FROM prompts GROUP BY source").fetchall()
    by_model = conn.execute("SELECT model, COUNT(*) as c FROM prompts WHERE model IS NOT NULL GROUP BY model").fetchall()
    by_tag_raw = conn.execute("SELECT tags FROM prompts WHERE tags IS NOT NULL AND tags != ''").fetchall()
    conn.close()

    tag_counts = {}
    for row in by_tag_raw:
        for t in row["tags"].split(","):
            t = t.strip()
            if t:
                tag_counts[t] = tag_counts.get(t, 0) + 1

    return {
        "total": total,
        "starred": starred,
        "by_source": {r["source"]: r["c"] for r in by_source},
        "by_model": {r["model"]: r["c"] for r in by_model},
        "by_tag": tag_counts,
    }


def list_top_used(limit=20):
    """Return prompts with use_count > 0, sorted by use_count descending."""
    conn = get_db()
    rows = conn.execute(
        "SELECT * FROM prompts WHERE use_count > 0 ORDER BY use_count DESC LIMIT ?",
        (limit,),
    ).fetchall()
    conn.close()
    return rows


def list_untagged(limit=100):
    """Return prompts that have no tags (NULL or empty string)."""
    conn = get_db()
    rows = conn.execute(
        "SELECT * FROM prompts WHERE tags IS NULL OR tags = '' LIMIT ?",
        (limit,),
    ).fetchall()
    conn.close()
    return rows


def prompt_exists_by_content(content_hash):
    """Check if a prompt with this content already exists (dedup by hash stored in source field)."""
    conn = get_db()
    row = conn.execute("SELECT id FROM prompts WHERE source LIKE ?", (f"%{content_hash}%",)).fetchone()
    conn.close()
    return row is not None