reprompt-cli 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
reprompt/storage/db.py ADDED
@@ -0,0 +1,314 @@
1
+ """SQLite storage for prompts, patterns, and term statistics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import sqlite3
8
+ from datetime import datetime, timedelta, timezone
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+
13
+ class PromptDB:
14
+ """SQLite-backed storage for prompt data."""
15
+
16
+ def __init__(self, path: Path) -> None:
17
+ self.path = path
18
+ path.parent.mkdir(parents=True, exist_ok=True)
19
+ self._init_schema()
20
+
21
+ def _conn(self) -> sqlite3.Connection:
22
+ """Get a connection with row_factory set."""
23
+ conn = sqlite3.connect(str(self.path))
24
+ conn.row_factory = sqlite3.Row
25
+ return conn
26
+
27
+ def _init_schema(self) -> None:
28
+ """Create tables if they don't exist."""
29
+ conn = self._conn()
30
+ try:
31
+ conn.executescript("""
32
+ CREATE TABLE IF NOT EXISTS prompts (
33
+ id INTEGER PRIMARY KEY,
34
+ hash TEXT UNIQUE,
35
+ text TEXT NOT NULL,
36
+ source TEXT NOT NULL,
37
+ project TEXT,
38
+ session_id TEXT,
39
+ timestamp TEXT,
40
+ char_count INTEGER,
41
+ embedding BLOB,
42
+ cluster_id INTEGER,
43
+ duplicate_of INTEGER REFERENCES prompts(id)
44
+ );
45
+ CREATE TABLE IF NOT EXISTS processed_sessions (
46
+ file_path TEXT PRIMARY KEY,
47
+ processed_at TEXT,
48
+ source TEXT
49
+ );
50
+ CREATE TABLE IF NOT EXISTS prompt_patterns (
51
+ id INTEGER PRIMARY KEY,
52
+ pattern_text TEXT,
53
+ frequency INTEGER,
54
+ avg_length REAL,
55
+ projects TEXT,
56
+ category TEXT,
57
+ first_seen TEXT,
58
+ last_seen TEXT,
59
+ examples TEXT
60
+ );
61
+ CREATE TABLE IF NOT EXISTS term_stats (
62
+ term TEXT PRIMARY KEY,
63
+ count INTEGER,
64
+ df INTEGER,
65
+ tfidf_avg REAL
66
+ );
67
+ """)
68
+ conn.commit()
69
+ finally:
70
+ conn.close()
71
+
72
+ @staticmethod
73
+ def _hash(text: str) -> str:
74
+ """SHA-256 hash of stripped text."""
75
+ return hashlib.sha256(text.strip().encode()).hexdigest()
76
+
77
+ def insert_prompt(
78
+ self,
79
+ text: str,
80
+ *,
81
+ source: str,
82
+ project: str | None = None,
83
+ session_id: str = "",
84
+ timestamp: str = "",
85
+ ) -> bool:
86
+ """Insert a prompt. Returns True if new, False if duplicate by hash."""
87
+ stripped = text.strip()
88
+ h = self._hash(stripped)
89
+ conn = self._conn()
90
+ try:
91
+ conn.execute(
92
+ """INSERT INTO prompts (hash, text, source, project, session_id,
93
+ timestamp, char_count) VALUES (?, ?, ?, ?, ?, ?, ?)""",
94
+ (h, stripped, source, project, session_id, timestamp, len(stripped)),
95
+ )
96
+ conn.commit()
97
+ return True
98
+ except sqlite3.IntegrityError:
99
+ return False
100
+ finally:
101
+ conn.close()
102
+
103
+ def get_all_prompts(self) -> list[dict[str, Any]]:
104
+ """Return all prompts as dicts."""
105
+ conn = self._conn()
106
+ try:
107
+ rows = conn.execute("SELECT * FROM prompts ORDER BY id").fetchall()
108
+ return [dict(r) for r in rows]
109
+ finally:
110
+ conn.close()
111
+
112
+ def get_prompts_without_embedding(self) -> list[dict[str, Any]]:
113
+ """Return prompts that have no embedding yet."""
114
+ conn = self._conn()
115
+ try:
116
+ rows = conn.execute(
117
+ "SELECT * FROM prompts WHERE embedding IS NULL AND duplicate_of IS NULL ORDER BY id"
118
+ ).fetchall()
119
+ return [dict(r) for r in rows]
120
+ finally:
121
+ conn.close()
122
+
123
+ def update_embedding(self, prompt_id: int, embedding: bytes) -> None:
124
+ """Store an embedding blob for a prompt."""
125
+ conn = self._conn()
126
+ try:
127
+ conn.execute("UPDATE prompts SET embedding = ? WHERE id = ?", (embedding, prompt_id))
128
+ conn.commit()
129
+ finally:
130
+ conn.close()
131
+
132
+ def mark_duplicate(self, prompt_id: int, duplicate_of: int) -> None:
133
+ """Mark a prompt as a duplicate of another."""
134
+ conn = self._conn()
135
+ try:
136
+ conn.execute(
137
+ "UPDATE prompts SET duplicate_of = ? WHERE id = ?", (duplicate_of, prompt_id)
138
+ )
139
+ conn.commit()
140
+ finally:
141
+ conn.close()
142
+
143
+ def mark_session_processed(self, file_path: str, source: str = "") -> None:
144
+ """Record that a session file has been processed."""
145
+ conn = self._conn()
146
+ try:
147
+ conn.execute(
148
+ "INSERT OR REPLACE INTO processed_sessions (file_path, processed_at, source) "
149
+ "VALUES (?, ?, ?)",
150
+ (file_path, datetime.now(timezone.utc).isoformat(), source),
151
+ )
152
+ conn.commit()
153
+ finally:
154
+ conn.close()
155
+
156
+ def is_session_processed(self, file_path: str) -> bool:
157
+ """Check if a session file has already been processed."""
158
+ conn = self._conn()
159
+ try:
160
+ row = conn.execute(
161
+ "SELECT 1 FROM processed_sessions WHERE file_path = ?", (file_path,)
162
+ ).fetchone()
163
+ return row is not None
164
+ finally:
165
+ conn.close()
166
+
167
+ def insert_pattern(
168
+ self,
169
+ pattern_text: str,
170
+ frequency: int,
171
+ avg_length: float,
172
+ projects: list[str],
173
+ category: str,
174
+ first_seen: str,
175
+ last_seen: str,
176
+ examples: list[str],
177
+ ) -> int:
178
+ """Insert a prompt pattern. Returns the new pattern ID."""
179
+ conn = self._conn()
180
+ try:
181
+ cursor = conn.execute(
182
+ """INSERT INTO prompt_patterns
183
+ (pattern_text, frequency, avg_length, projects, category,
184
+ first_seen, last_seen, examples)
185
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
186
+ (
187
+ pattern_text,
188
+ frequency,
189
+ avg_length,
190
+ json.dumps(projects),
191
+ category,
192
+ first_seen,
193
+ last_seen,
194
+ json.dumps(examples),
195
+ ),
196
+ )
197
+ conn.commit()
198
+ pattern_id = cursor.lastrowid
199
+ assert pattern_id is not None
200
+ return pattern_id
201
+ finally:
202
+ conn.close()
203
+
204
+ def get_patterns(self, category: str | None = None) -> list[dict[str, Any]]:
205
+ """Return all patterns, optionally filtered by category."""
206
+ conn = self._conn()
207
+ try:
208
+ if category:
209
+ rows = conn.execute(
210
+ "SELECT * FROM prompt_patterns WHERE category = ? ORDER BY frequency DESC",
211
+ (category,),
212
+ ).fetchall()
213
+ else:
214
+ rows = conn.execute(
215
+ "SELECT * FROM prompt_patterns ORDER BY frequency DESC"
216
+ ).fetchall()
217
+ result = []
218
+ for r in rows:
219
+ d = dict(r)
220
+ d["projects"] = json.loads(d["projects"]) if d["projects"] else []
221
+ d["examples"] = json.loads(d["examples"]) if d["examples"] else []
222
+ result.append(d)
223
+ return result
224
+ finally:
225
+ conn.close()
226
+
227
+ def clear_patterns(self) -> None:
228
+ """Delete all stored patterns (called before re-computing)."""
229
+ conn = self._conn()
230
+ try:
231
+ conn.execute("DELETE FROM prompt_patterns")
232
+ conn.commit()
233
+ finally:
234
+ conn.close()
235
+
236
+ def upsert_term_stats(self, term: str, count: int, df: int, tfidf_avg: float) -> None:
237
+ """Insert or update term statistics."""
238
+ conn = self._conn()
239
+ try:
240
+ conn.execute(
241
+ """INSERT INTO term_stats (term, count, df, tfidf_avg)
242
+ VALUES (?, ?, ?, ?)
243
+ ON CONFLICT(term) DO UPDATE SET
244
+ count = excluded.count,
245
+ df = excluded.df,
246
+ tfidf_avg = excluded.tfidf_avg""",
247
+ (term, count, df, tfidf_avg),
248
+ )
249
+ conn.commit()
250
+ finally:
251
+ conn.close()
252
+
253
+ def get_term_stats(self, limit: int = 50) -> list[dict[str, Any]]:
254
+ """Return top terms by count."""
255
+ conn = self._conn()
256
+ try:
257
+ rows = conn.execute(
258
+ "SELECT * FROM term_stats ORDER BY count DESC LIMIT ?", (limit,)
259
+ ).fetchall()
260
+ return [dict(r) for r in rows]
261
+ finally:
262
+ conn.close()
263
+
264
+ def purge_old_prompts(self, retention_days: int = 90) -> int:
265
+ """Delete prompts older than retention_days. Returns count deleted.
266
+
267
+ Two-pass: first remove duplicates pointing to old prompts,
268
+ then remove the old prompts themselves.
269
+ """
270
+ cutoff = (datetime.now(timezone.utc) - timedelta(days=retention_days)).isoformat()
271
+ conn = self._conn()
272
+ try:
273
+ # Pass 1: clear duplicate_of references to soon-deleted prompts
274
+ conn.execute(
275
+ """UPDATE prompts SET duplicate_of = NULL
276
+ WHERE duplicate_of IN (
277
+ SELECT id FROM prompts WHERE timestamp < ? AND timestamp != ''
278
+ )""",
279
+ (cutoff,),
280
+ )
281
+ # Pass 2: delete old prompts
282
+ cursor = conn.execute(
283
+ "DELETE FROM prompts WHERE timestamp < ? AND timestamp != ''",
284
+ (cutoff,),
285
+ )
286
+ deleted = cursor.rowcount
287
+ conn.commit()
288
+ return deleted
289
+ finally:
290
+ conn.close()
291
+
292
+ def get_stats(self) -> dict[str, Any]:
293
+ """Return summary statistics."""
294
+ conn = self._conn()
295
+ try:
296
+ total = conn.execute("SELECT COUNT(*) FROM prompts").fetchone()[0]
297
+ unique = conn.execute(
298
+ "SELECT COUNT(*) FROM prompts WHERE duplicate_of IS NULL"
299
+ ).fetchone()[0]
300
+ sessions = conn.execute("SELECT COUNT(*) FROM processed_sessions").fetchone()[0]
301
+ patterns = conn.execute("SELECT COUNT(*) FROM prompt_patterns").fetchone()[0]
302
+ date_range = conn.execute(
303
+ "SELECT MIN(timestamp), MAX(timestamp) FROM prompts WHERE timestamp != ''"
304
+ ).fetchone()
305
+ return {
306
+ "total_prompts": total,
307
+ "unique_prompts": unique,
308
+ "sessions_processed": sessions,
309
+ "patterns": patterns,
310
+ "earliest": date_range[0] if date_range else None,
311
+ "latest": date_range[1] if date_range else None,
312
+ }
313
+ finally:
314
+ conn.close()
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: reprompt-cli
3
+ Version: 0.1.1
4
+ Summary: Discover, analyze, and evolve your best prompts from AI coding sessions
5
+ Project-URL: Homepage, https://github.com/reprompt-dev/reprompt
6
+ Project-URL: Repository, https://github.com/reprompt-dev/reprompt
7
+ Project-URL: Issues, https://github.com/reprompt-dev/reprompt/issues
8
+ Project-URL: Changelog, https://github.com/reprompt-dev/reprompt/blob/main/CHANGELOG.md
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai,analytics,claude-code,cli,llm,prompt
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: pydantic-settings>=2.0
25
+ Requires-Dist: rich>=13.0
26
+ Requires-Dist: scikit-learn>=1.4
27
+ Requires-Dist: typer>=0.9
28
+ Provides-Extra: dev
29
+ Requires-Dist: mypy>=1.0; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.4; extra == 'dev'
33
+ Provides-Extra: local
34
+ Requires-Dist: sentence-transformers>=2.0; extra == 'local'
35
+ Provides-Extra: ollama
36
+ Requires-Dist: requests>=2.31; extra == 'ollama'
37
+ Provides-Extra: openai
38
+ Requires-Dist: openai>=1.0; extra == 'openai'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # reprompt
42
+
43
+ [![CI](https://github.com/reprompt-dev/reprompt/actions/workflows/ci.yml/badge.svg)](https://github.com/reprompt-dev/reprompt/actions/workflows/ci.yml)
44
+ [![PyPI version](https://img.shields.io/pypi/v/reprompt-cli)](https://pypi.org/project/reprompt-cli/)
45
+ [![Python](https://img.shields.io/pypi/pyversions/reprompt-cli)](https://pypi.org/project/reprompt-cli/)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
+
48
+ > Discover, analyze, and evolve your best prompts from AI coding sessions.
49
+
50
+ Every developer's AI session history contains reusable prompt patterns -- scattered across hundreds of session files. **reprompt** extracts them, deduplicates, analyzes frequency, and builds a personal prompt library that evolves over time.
51
+
52
+ ## Quick Start
53
+
54
+ ```bash
55
+ pipx install reprompt-cli
56
+ reprompt scan
57
+ reprompt report
58
+ reprompt library
59
+ ```
60
+
61
+ ## Features
62
+
63
+ - **Auto-detection** -- finds Claude Code and OpenClaw sessions automatically
64
+ - **Two-layer dedup** -- SHA-256 exact + TF-IDF semantic similarity
65
+ - **Hot terms analysis** -- TF-IDF discovers your most-used technical terms
66
+ - **K-means clustering** -- groups similar prompts into themes
67
+ - **Prompt library** -- extracts high-frequency patterns, auto-categorizes (debug/implement/test/review/refactor/explain/config)
68
+ - **Rich reports** -- beautiful terminal output with tables and bar charts
69
+ - **Multiple formats** -- terminal, JSON (for pipelines), Markdown (for docs)
70
+ - **Pluggable adapters** -- add support for any AI coding tool
71
+ - **Zero config** -- works out of the box, customize via env vars or TOML
72
+
73
+ ## Supported AI Tools
74
+
75
+ | Tool | Status | Session Path |
76
+ |------|--------|-------------|
77
+ | Claude Code | Supported | `~/.claude/projects/` |
78
+ | OpenClaw / OpenCode | Supported | `~/.opencode/sessions/` |
79
+ | Cursor | Planned | -- |
80
+ | Codex CLI | Planned | -- |
81
+ | Gemini CLI | Planned | -- |
82
+
83
+ ## Usage
84
+
85
+ ```bash
86
+ # Scan all detected AI tools
87
+ reprompt scan
88
+
89
+ # Scan specific source
90
+ reprompt scan --source claude-code
91
+
92
+ # Scan custom path
93
+ reprompt scan --path ~/custom/sessions
94
+
95
+ # Rich terminal report
96
+ reprompt report
97
+
98
+ # JSON output (for CI/pipelines)
99
+ reprompt report --format json
100
+
101
+ # View your prompt library
102
+ reprompt library
103
+
104
+ # Filter by category
105
+ reprompt library --category debug
106
+
107
+ # Export prompt library as Markdown
108
+ reprompt library prompts.md
109
+
110
+ # Database stats
111
+ reprompt status
112
+
113
+ # Auto-scan after sessions
114
+ reprompt install-hook
115
+
116
+ # Cleanup old data
117
+ reprompt purge --older-than 90d
118
+ ```
119
+
120
+ ## Terminal Report
121
+
122
+ ```
123
+ reprompt -- AI Session Analytics
124
+ ========================================
125
+
126
+ Overview
127
+ Total prompts: 1,247
128
+ Unique (deduped): 832
129
+ Sessions scanned: 156
130
+ Sources: claude-code, openclaw
131
+
132
+ Top Prompt Patterns
133
+ # | Pattern | Count | Category
134
+ 1 | fix the failing test... | 42 | debug
135
+ 2 | add unit tests for... | 38 | test
136
+ 3 | refactor X to use... | 27 | refactor
137
+ ```
138
+
139
+ ## Configuration
140
+
141
+ Zero config by default. Customize with environment variables or TOML:
142
+
143
+ ```bash
144
+ # Environment variables (prefix: REPROMPT_)
145
+ REPROMPT_EMBEDDING_BACKEND=ollama reprompt scan
146
+ REPROMPT_DB_PATH=~/custom/reprompt.db reprompt status
147
+ ```
148
+
149
+ ```toml
150
+ # ~/.config/reprompt/config.toml
151
+ [embedding]
152
+ backend = "tfidf" # tfidf | ollama | local | openai
153
+
154
+ [storage]
155
+ db_path = "~/.local/share/reprompt/reprompt.db"
156
+
157
+ [dedup]
158
+ semantic_threshold = 0.85
159
+
160
+ [library]
161
+ min_frequency = 3
162
+ ```
163
+
164
+ ## Optional Backends
165
+
166
+ ```bash
167
+ pip install reprompt-cli[ollama] # Ollama API embeddings
168
+ pip install reprompt-cli[local] # sentence-transformers (CPU)
169
+ pip install reprompt-cli[openai] # OpenAI API embeddings
170
+ ```
171
+
172
+ ## Adding an Adapter
173
+
174
+ Create a new adapter by subclassing `BaseAdapter`:
175
+
176
+ ```python
177
+ from reprompt.adapters.base import BaseAdapter
178
+ from reprompt.core.models import Prompt
179
+
180
+ class MyToolAdapter(BaseAdapter):
181
+ name = "my-tool"
182
+ default_session_path = "~/.my-tool/sessions"
183
+
184
+ def parse_session(self, path):
185
+ # Parse session file -> list[Prompt]
186
+ ...
187
+
188
+ def detect_installed(self):
189
+ return Path(self.default_session_path).expanduser().exists()
190
+ ```
191
+
192
+ ## Contributing
193
+
194
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
195
+
196
+ ## License
197
+
198
+ MIT
@@ -0,0 +1,29 @@
1
+ reprompt/__init__.py,sha256=ssgV9xNer2bW8O0achjE8LP17vwUmTOMVoX9_T4Ute4,149
2
+ reprompt/cli.py,sha256=GrI_21s6QOpdrcjnF2ERY2N_nJt6eMchPKbOH8QYbDU,5849
3
+ reprompt/config.py,sha256=Wt5MqVKC18jU07lAsa6wKqSktw7FB2k-Kb6Uw7sVLVg,1061
4
+ reprompt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ reprompt/adapters/__init__.py,sha256=_LbAhUS_85bD9weFW9LnM-4x-EkIo-sTPZGP6z1fgVM,88
6
+ reprompt/adapters/base.py,sha256=rdG6yUDB7EWw7i066l2V6WmBE663LNAUdJ29sF3iveU,617
7
+ reprompt/adapters/claude_code.py,sha256=jLQ2zog2N0HBEaeq2xS9k4th5a58UajHmJ601wj8zSI,3825
8
+ reprompt/adapters/openclaw.py,sha256=_yVRT5eKYkE6LB9zGmm-obbqzYg4BRlf3fdO6o52jkM,2555
9
+ reprompt/core/__init__.py,sha256=GuzztUmW-OJWi7iC_4IRg3L_tGAe-dcdyP4URC_s7KE,36
10
+ reprompt/core/analyzer.py,sha256=E9qnq3eHTie5EPRXw0Y6qlVHWlXNnNubQM79dEQ5NM4,2050
11
+ reprompt/core/dedup.py,sha256=z_3_qLjxvqmRJhWr53KkdOCzdaCGs2X_D3vhHSjkS04,2855
12
+ reprompt/core/library.py,sha256=mPIOdz0unBEfuB_fNTzBwkVEv3SVplFg_pPvVavLsXk,3268
13
+ reprompt/core/models.py,sha256=eJsiBe_nN6BkjKCEVy_x12EayVEVa2AlvKVtDuzSKgI,568
14
+ reprompt/core/pipeline.py,sha256=j59JNJZDzzqii9wstpyiwnCHvVjcuK-qLdJhov2SdRw,5209
15
+ reprompt/embeddings/__init__.py,sha256=l1ssUD_h7Y0Ax1lAx3-KI9RUGM1oTFpibDWBSMP-DEM,86
16
+ reprompt/embeddings/base.py,sha256=XvftNdsWiSg67JE_W8zpmO0NL2KJhc0VhYnOH0167Ms,629
17
+ reprompt/embeddings/ollama.py,sha256=Rpi54N4q3t5pwrDzQIy-YaVFoMFwsFH2gHSKjYoovi4,1549
18
+ reprompt/embeddings/tfidf.py,sha256=gJd9Tvf35Nxwbm9Zxm9iSE4tUrAlBcBpRl_FVA-PgFo,702
19
+ reprompt/output/__init__.py,sha256=xwGXiWjcpVgKoLDYwAdN9LbpviSKNnO1qgUBexj5IhQ,46
20
+ reprompt/output/json_out.py,sha256=ip70Oj2EImHepevKCPtf_ztBYH98FEQwVJcDKW96kT0,249
21
+ reprompt/output/markdown.py,sha256=4nCXiBYKS0NQqD_h2p71sqDzWKPFkn1fj1XOfqEuXh0,1656
22
+ reprompt/output/terminal.py,sha256=UFFy715T6IeYZiAz24iIg3ql_Y8Pz6f-mHY5CkO4PMk,2383
23
+ reprompt/storage/__init__.py,sha256=etUD91sg2hoIGrPQUSZWI6jTbaOc_d-lWjlmrxvF9Mk,40
24
+ reprompt/storage/db.py,sha256=Yoh_9G_EC7nfVj1uoODltbVUQHg_UZNxV8BM6iqF1IM,11061
25
+ reprompt_cli-0.1.1.dist-info/METADATA,sha256=VhPTPVFIPe-F1bDXrl6Wmr5nv88Hc3A-BVArE7UuG_0,5848
26
+ reprompt_cli-0.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
27
+ reprompt_cli-0.1.1.dist-info/entry_points.txt,sha256=VQTi0S-vVU8YIfEZU_aOm6SHBpRPuN3Wh7MzLhemb_Y,46
28
+ reprompt_cli-0.1.1.dist-info/licenses/LICENSE,sha256=5xJBBHKoDOoUfG1iXzCmBScu7I-L4qIxWEm8jaYnzgo,1069
29
+ reprompt_cli-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ reprompt = reprompt.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 reprompt-dev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.