ai-cli-toolkit 0.2.0__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.
@@ -0,0 +1,236 @@
1
+ """Copilot session-store SQLite access helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sqlite3
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ # Default location of the Copilot CLI session store database.
11
+ SESSION_STORE_PATHS = (
12
+ Path.home() / ".copilot" / "session-store.db",
13
+ )
14
+
15
+
16
+ def _normalize_cwd(value: str) -> str:
17
+ text = value.strip()
18
+ if not text:
19
+ return ""
20
+ try:
21
+ path = Path(text).expanduser()
22
+ if path.exists():
23
+ return str(path.resolve())
24
+ return str(path)
25
+ except OSError:
26
+ return text
27
+
28
+
29
+ def find_session_store_db(path: str = "") -> Path | None:
30
+ """Locate the Copilot CLI session store database."""
31
+ if path:
32
+ p = Path(path).expanduser()
33
+ if p.is_file():
34
+ return p
35
+ for candidate in SESSION_STORE_PATHS:
36
+ if candidate.is_file():
37
+ return candidate
38
+ return None
39
+
40
+
41
+ def _connect_store(db_path: Path) -> sqlite3.Connection:
42
+ """Open a read-only connection to the session store."""
43
+ uri = f"file:{db_path}?mode=ro"
44
+ conn = sqlite3.connect(uri, uri=True)
45
+ conn.row_factory = sqlite3.Row
46
+ return conn
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class StoreSession:
51
+ """A session record from the session store database."""
52
+
53
+ id: str
54
+ cwd: str
55
+ repository: str
56
+ branch: str
57
+ summary: str
58
+ created_at: str
59
+ updated_at: str
60
+
61
+
62
+ def list_store_sessions(
63
+ db_path: Path,
64
+ cwd: str = "",
65
+ branch: str = "",
66
+ limit: int = 50,
67
+ ) -> list[StoreSession]:
68
+ """List sessions from the store, optionally filtered by cwd or branch."""
69
+ conn = _connect_store(db_path)
70
+ try:
71
+ clauses: list[str] = []
72
+ params: list[Any] = []
73
+ if cwd:
74
+ norm = _normalize_cwd(cwd)
75
+ clauses.append("(cwd = ? OR cwd LIKE ?)")
76
+ params.extend([norm, norm + "/%"])
77
+ if branch:
78
+ clauses.append("branch = ?")
79
+ params.append(branch)
80
+ where = (" WHERE " + " AND ".join(clauses)) if clauses else ""
81
+ query = (
82
+ "SELECT id, cwd, repository, branch, summary, created_at, updated_at "
83
+ f"FROM sessions{where} ORDER BY created_at DESC LIMIT ?"
84
+ )
85
+ params.append(limit)
86
+ rows = conn.execute(query, params).fetchall()
87
+ return [
88
+ StoreSession(
89
+ id=r["id"],
90
+ cwd=r["cwd"] or "",
91
+ repository=r["repository"] or "",
92
+ branch=r["branch"] or "",
93
+ summary=r["summary"] or "",
94
+ created_at=r["created_at"] or "",
95
+ updated_at=r["updated_at"] or "",
96
+ )
97
+ for r in rows
98
+ ]
99
+ finally:
100
+ conn.close()
101
+
102
+
103
+ def query_store_turns(
104
+ db_path: Path,
105
+ session_id: str = "",
106
+ grep: str = "",
107
+ limit: int = 200,
108
+ ) -> list[dict[str, Any]]:
109
+ """Retrieve conversation turns from the session store."""
110
+ conn = _connect_store(db_path)
111
+ try:
112
+ clauses: list[str] = []
113
+ params: list[Any] = []
114
+ if session_id:
115
+ clauses.append("t.session_id = ?")
116
+ params.append(session_id)
117
+ if grep:
118
+ clauses.append("(t.user_message LIKE ? OR t.assistant_response LIKE ?)")
119
+ needle = f"%{grep}%"
120
+ params.extend([needle, needle])
121
+ where = (" WHERE " + " AND ".join(clauses)) if clauses else ""
122
+ query = (
123
+ "SELECT t.session_id, t.turn_index, t.user_message, t.assistant_response, "
124
+ "t.timestamp, s.cwd, s.branch "
125
+ "FROM turns t JOIN sessions s ON t.session_id = s.id"
126
+ f"{where} ORDER BY t.timestamp, t.turn_index LIMIT ?"
127
+ )
128
+ params.append(limit)
129
+ rows = conn.execute(query, params).fetchall()
130
+
131
+ messages: list[dict[str, Any]] = []
132
+ for r in rows:
133
+ sid = r["session_id"]
134
+ ts = r["timestamp"] or ""
135
+ if r["user_message"]:
136
+ messages.append({
137
+ "agent": "copilot",
138
+ "role": "user",
139
+ "type": "text",
140
+ "content": r["user_message"],
141
+ "line": r["turn_index"],
142
+ "timestamp": ts,
143
+ "file": f"session-store:{sid}",
144
+ "session_id": sid,
145
+ })
146
+ if r["assistant_response"]:
147
+ messages.append({
148
+ "agent": "copilot",
149
+ "role": "assistant",
150
+ "type": "text",
151
+ "content": r["assistant_response"],
152
+ "line": r["turn_index"],
153
+ "timestamp": ts,
154
+ "file": f"session-store:{sid}",
155
+ "session_id": sid,
156
+ })
157
+ return messages
158
+ finally:
159
+ conn.close()
160
+
161
+
162
+ def search_store(
163
+ db_path: Path,
164
+ query: str,
165
+ limit: int = 30,
166
+ ) -> list[dict[str, Any]]:
167
+ """Full-text search across the session store using FTS5."""
168
+ conn = _connect_store(db_path)
169
+ try:
170
+ rows = conn.execute(
171
+ "SELECT content, session_id, source_type, source_id "
172
+ "FROM search_index WHERE search_index MATCH ? "
173
+ "ORDER BY rank LIMIT ?",
174
+ [query, limit],
175
+ ).fetchall()
176
+
177
+ results: list[dict[str, Any]] = []
178
+ for r in rows:
179
+ results.append({
180
+ "agent": "copilot",
181
+ "role": "assistant",
182
+ "type": "text",
183
+ "content": r["content"][:2000] if r["content"] else "",
184
+ "line": 0,
185
+ "timestamp": "",
186
+ "file": f"session-store:{r['session_id']}",
187
+ "session_id": r["session_id"],
188
+ "source_type": r["source_type"],
189
+ })
190
+ return results
191
+ finally:
192
+ conn.close()
193
+
194
+
195
+ def query_store_checkpoints(
196
+ db_path: Path,
197
+ session_id: str,
198
+ ) -> list[dict[str, Any]]:
199
+ """Retrieve checkpoints for a session from the store."""
200
+ conn = _connect_store(db_path)
201
+ try:
202
+ rows = conn.execute(
203
+ "SELECT checkpoint_number, title, overview, work_done, next_steps "
204
+ "FROM checkpoints WHERE session_id = ? ORDER BY checkpoint_number",
205
+ [session_id],
206
+ ).fetchall()
207
+ return [dict(r) for r in rows]
208
+ finally:
209
+ conn.close()
210
+
211
+
212
+ def query_store_files(
213
+ db_path: Path,
214
+ session_id: str = "",
215
+ file_pattern: str = "",
216
+ ) -> list[dict[str, Any]]:
217
+ """Retrieve file records from the session store."""
218
+ conn = _connect_store(db_path)
219
+ try:
220
+ clauses: list[str] = []
221
+ params: list[Any] = []
222
+ if session_id:
223
+ clauses.append("sf.session_id = ?")
224
+ params.append(session_id)
225
+ if file_pattern:
226
+ clauses.append("sf.file_path LIKE ?")
227
+ params.append(f"%{file_pattern}%")
228
+ where = (" WHERE " + " AND ".join(clauses)) if clauses else ""
229
+ rows = conn.execute(
230
+ "SELECT sf.session_id, sf.file_path, sf.tool_name, sf.first_seen_at "
231
+ f"FROM session_files sf{where} ORDER BY sf.first_seen_at DESC LIMIT 100",
232
+ params,
233
+ ).fetchall()
234
+ return [dict(r) for r in rows]
235
+ finally:
236
+ conn.close()