gora-cli 0.1.2__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.
gora/tui.py ADDED
@@ -0,0 +1,115 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+
9
+ TUI_CACHE_VERSION = "bubbletea-v1"
10
+
11
+
12
+ def run_tui(db_path: Path | None) -> int:
13
+ command, cwd = _tui_command()
14
+ if command is None:
15
+ print(
16
+ "Bubble Tea TUI is not built and Go is not available.\n"
17
+ "Build it with: go -C gora/go_tui build -o ../../dist/gora-tui .\n"
18
+ "Then run: GORA_TUI_BIN=dist/gora-tui gora",
19
+ file=sys.stderr,
20
+ )
21
+ return 1
22
+
23
+ if db_path is not None:
24
+ command.extend(["--db", str(db_path)])
25
+
26
+ try:
27
+ return subprocess.run(command, cwd=cwd).returncode
28
+ except KeyboardInterrupt:
29
+ return 130
30
+ except OSError as exc:
31
+ print(f"failed to launch Bubble Tea TUI: {exc}", file=sys.stderr)
32
+ return 1
33
+
34
+
35
+ def _tui_command() -> tuple[list[str] | None, Path | None]:
36
+ configured = os.environ.get("GORA_TUI_BIN")
37
+ if configured:
38
+ return [_expand(configured)], None
39
+
40
+ discovered = shutil.which("gora-tui")
41
+ if discovered:
42
+ return [discovered], None
43
+
44
+ repo_root = Path(__file__).resolve().parents[1]
45
+ for candidate in (
46
+ repo_root / "dist" / "gora-tui",
47
+ repo_root / "bin" / "gora-tui",
48
+ ):
49
+ if candidate.exists() and os.access(candidate, os.X_OK):
50
+ return [str(candidate)], None
51
+
52
+ source_dir = Path(__file__).resolve().parent / "go_tui"
53
+ if source_dir.exists() and shutil.which("go"):
54
+ cached = _cached_tui_path()
55
+ if not cached.exists() or _source_is_newer(source_dir, cached):
56
+ if _build_tui(source_dir, cached):
57
+ return [str(cached)], None
58
+ return ["go", "run", "."], source_dir
59
+ return [str(cached)], None
60
+
61
+ return None, None
62
+
63
+
64
+ def _expand(path: str) -> str:
65
+ return str(Path(path).expanduser())
66
+
67
+
68
+ def _cached_tui_path() -> Path:
69
+ cache_home = os.environ.get("XDG_CACHE_HOME")
70
+ if cache_home:
71
+ root = Path(cache_home).expanduser()
72
+ elif sys.platform == "darwin":
73
+ root = Path.home() / "Library" / "Caches"
74
+ else:
75
+ root = Path.home() / ".cache"
76
+ return root / "gora" / TUI_CACHE_VERSION / "gora-tui"
77
+
78
+
79
+ def _source_is_newer(source_dir: Path, binary: Path) -> bool:
80
+ try:
81
+ binary_mtime = binary.stat().st_mtime
82
+ except OSError:
83
+ return True
84
+ for path in source_dir.rglob("*"):
85
+ if path.is_file() and path.suffix in {".go", ".mod", ".sum"}:
86
+ try:
87
+ if path.stat().st_mtime > binary_mtime:
88
+ return True
89
+ except OSError:
90
+ return True
91
+ return False
92
+
93
+
94
+ def _build_tui(source_dir: Path, target: Path) -> bool:
95
+ target.parent.mkdir(parents=True, exist_ok=True)
96
+ tmp = target.with_suffix(".tmp")
97
+ try:
98
+ result = subprocess.run(
99
+ ["go", "build", "-o", str(tmp), "."],
100
+ cwd=source_dir,
101
+ stdout=subprocess.DEVNULL,
102
+ stderr=subprocess.DEVNULL,
103
+ )
104
+ if result.returncode != 0:
105
+ return False
106
+ tmp.replace(target)
107
+ target.chmod(0o755)
108
+ return True
109
+ except OSError:
110
+ return False
111
+ finally:
112
+ try:
113
+ tmp.unlink()
114
+ except OSError:
115
+ pass
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: gora-cli
3
+ Version: 0.1.2
4
+ Summary: Local CLI chat history index for Codex, Claude Code, and Pi.
5
+ Classifier: Environment :: Console
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Topic :: Software Development
10
+ Classifier: Topic :: Terminals
11
+ Requires-Python: >=3.13
12
+ Description-Content-Type: text/markdown
13
+
14
+ <p align="center">
15
+ <img src="images/gora-header.png" alt="Gora header" width="100%">
16
+ </p>
17
+
18
+ # gora
19
+
20
+ Local memory for coding-agent chats.
21
+
22
+ `gora` indexes chat history from Codex, Claude Code, and Pi into one local
23
+ SQLite archive. Use it when you need to remember what you asked a model, what a
24
+ coding agent changed, which repo a conversation belonged to, or which harness
25
+ and model were used.
26
+
27
+ ## Why
28
+
29
+ Coding-agent history is spread across different local folders. Some harnesses
30
+ also prune or rewrite old logs. `gora` gives you one place to search them.
31
+
32
+ Common uses:
33
+
34
+ - Find an old debugging session by keyword.
35
+ - Check what a coding agent did in a repo last week.
36
+ - Recover a command, error, plan, or explanation from a past chat.
37
+ - See tool calls and command output inline with the chat transcript.
38
+ - Search across Codex, Claude Code, and Pi at the same time.
39
+ - Filter history by harness, repo, role, or model.
40
+
41
+ ## Supported History
42
+
43
+ `gora` reads local history files from:
44
+
45
+ ```text
46
+ Codex ~/.codex/sessions/**/*.jsonl
47
+ Claude Code ~/.claude/projects/**/*.jsonl
48
+ Pi ~/.pi/agent/sessions/**/*.jsonl
49
+ ```
50
+
51
+ No cloud service is required. The index stays on your machine.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ uv tool install gora-cli
57
+ gora
58
+ ```
59
+
60
+ The PyPI package is `gora-cli`. The installed command is `gora`.
61
+
62
+ Install directly from GitHub:
63
+
64
+ ```bash
65
+ uv tool install git+https://github.com/mertdeveci5/gora
66
+ ```
67
+
68
+ For local development:
69
+
70
+ ```bash
71
+ git clone <repo-url>
72
+ cd gora
73
+ uv tool install --editable .
74
+ ```
75
+
76
+ ## Quick Start
77
+
78
+ 1. Check what Gora can see:
79
+
80
+ ```bash
81
+ gora doctor
82
+ ```
83
+
84
+ 2. Import local history:
85
+
86
+ ```bash
87
+ gora import
88
+ ```
89
+
90
+ 3. Open the terminal UI:
91
+
92
+ ```bash
93
+ gora
94
+ ```
95
+
96
+ 4. Search from the CLI:
97
+
98
+ ```bash
99
+ gora search "render deploy"
100
+ gora search "ctx7 login" --provider codex
101
+ gora search "database migration" --model "model-from-gora-models"
102
+ ```
103
+
104
+ 5. Show a full transcript:
105
+
106
+ ```bash
107
+ gora show codex:<session-id>
108
+ ```
109
+
110
+ ## Terminal UI
111
+
112
+ Run:
113
+
114
+ ```bash
115
+ gora
116
+ ```
117
+
118
+ The interactive UI is a Bubble Tea app built with Bubbles components. Gora
119
+ builds a cached local helper from the bundled Go source on first launch when Go
120
+ is available. For a prebuilt local helper:
121
+
122
+ ```bash
123
+ go -C gora/go_tui build -o ../../dist/gora-tui .
124
+ GORA_TUI_BIN=dist/gora-tui gora
125
+ ```
126
+
127
+ Controls:
128
+
129
+ ```text
130
+ Type search chats
131
+ Tab move focus between search, filters, and results
132
+ Ctrl-F open the filter editor
133
+ Enter open the selected chat or edit focused filters
134
+ Click Filters open the filter editor
135
+ Space select or clear a filter option in the filter editor
136
+ 1/H 2/R 3/M 4/O jump between filter editor steps
137
+ Enter select a filter option and move to the next filter step
138
+ Up/Down move through results or filter options
139
+ M export the selected chat as Markdown from results/transcript
140
+ T export the selected chat as plain text from results/transcript
141
+ Left/Esc return from transcript or apply filters and go back
142
+ Ctrl-U clear search or all filters in the filter editor
143
+ Esc clear search, clear active filters, or quit
144
+ ```
145
+
146
+ The filter editor supports multi-select harnesses, repos, and models. For
147
+ example, select Claude Code and two repos to show only Claude Code sessions from
148
+ those repos.
149
+
150
+ Exports are written to `~/Downloads/gora-exports` and keep the transcript in a
151
+ portable Markdown or plain-text file.
152
+
153
+ ## CLI Commands
154
+
155
+ ```bash
156
+ gora doctor
157
+ gora import
158
+ gora harnesses
159
+ gora repos
160
+ gora models
161
+ gora recent
162
+ gora list --limit 20
163
+ gora search "query"
164
+ gora show <session>
165
+ ```
166
+
167
+ Filters:
168
+
169
+ ```bash
170
+ gora list --provider codex
171
+ gora list --cwd tlmc
172
+ gora list --model "model-from-gora-models"
173
+ gora list --include-children
174
+ gora search "auth bug" --provider claude --model "model-from-gora-models"
175
+ gora search "review output" --include-children
176
+ gora show <session> --role user --role assistant
177
+ ```
178
+
179
+ Most list-style commands print numbered results and a quick command for the top
180
+ result.
181
+
182
+ Example:
183
+
184
+ ```bash
185
+ gora models
186
+ ```
187
+
188
+ ```text
189
+ 1. Model: gpt-5.4
190
+ Provider: openai
191
+ Chats: 460
192
+ Messages: 43079
193
+
194
+ 2. Model: gpt-5.3-codex
195
+ Provider: openai
196
+ Chats: 333
197
+ Messages: 14585
198
+
199
+ Quick command:
200
+ gora search "<query>" --model gpt-5.4
201
+ ```
202
+
203
+ Useful one-shot commands:
204
+
205
+ ```bash
206
+ gora harnesses
207
+ gora repos --limit 10
208
+ gora models --limit 10
209
+ gora recent --provider codex --limit 5
210
+ gora search "migration bug" --cwd tlmc
211
+ gora search "auth bug" --model "model-from-gora-models"
212
+ gora show claude:<session-id>
213
+ ```
214
+
215
+ ## Import Behavior
216
+
217
+ `gora import` is archive-oriented.
218
+
219
+ Once a chat is imported, Gora keeps it searchable even if the original harness
220
+ later deletes, prunes, or rewrites its local log file. Refreshing a changed
221
+ source file merges in new messages but does not delete messages already in the
222
+ archive.
223
+
224
+ Tool calls and tool-result output are imported by default so transcripts show
225
+ what the agent actually ran. Gora redacts common token shapes before storing
226
+ message text.
227
+
228
+ Codex can create child rollout files for subagents and review tasks. Gora stores
229
+ those records with their parent thread metadata, but normal list, recent, repos,
230
+ search, and TUI views show root user chats by default. Use `--include-children`
231
+ on CLI list and search style commands when you want to inspect child rollout
232
+ records too.
233
+
234
+ ## Models
235
+
236
+ Gora does not use a fixed model-name list.
237
+
238
+ It extracts model metadata from each harness's logged structure:
239
+
240
+ ```text
241
+ Codex turn context model metadata
242
+ Claude Code assistant message model metadata
243
+ Pi model change records and message metadata
244
+ ```
245
+
246
+ List indexed models:
247
+
248
+ ```bash
249
+ gora models
250
+ ```
251
+
252
+ Filter by an indexed model:
253
+
254
+ ```bash
255
+ gora search "cache bug" --model gpt-5.5
256
+ ```
257
+
258
+ ## Data Location
259
+
260
+ Default index path:
261
+
262
+ ```text
263
+ macOS ~/Library/Application Support/gora/history.sqlite
264
+ Linux ~/.local/share/gora/history.sqlite
265
+ ```
266
+
267
+ Use another path:
268
+
269
+ ```bash
270
+ GORA_DB=/path/to/history.sqlite gora import
271
+ gora --db /path/to/history.sqlite search "query"
272
+ ```
273
+
274
+ The default data directory is created with owner-only permissions.
275
+
276
+ ## Development
277
+
278
+ ```bash
279
+ uv run python -m unittest
280
+ uv run python -m compileall gora tests main.py
281
+ uv build
282
+ ```
@@ -0,0 +1,14 @@
1
+ gora/__init__.py,sha256=ulCdHq_2DQHHaXDkXTSVf-8D_07_2YRnB18hxBqrWNw,104
2
+ gora/__main__.py,sha256=yXtZIqonLnM8h-9OOsl9Bt4USaF-mVaqDS-BZeQchC4,282
3
+ gora/cli.py,sha256=JbC35xeuoY501NsOcHxJoXDMBu1Q0dsTLegRG2wbAk0,16741
4
+ gora/parsers.py,sha256=ALLNZoIn0jhINjqsVVZkvv_74hRlCR51Eclkz9M2nM4,19505
5
+ gora/store.py,sha256=f-JtJHTSj9oL19sBXdHbKhkW8m1fNeRFf4cHpIEBYgk,30038
6
+ gora/tui.py,sha256=7EUeAE_gKor2R3AuUM9FyTcqgrv5FCqHiGtKGx8TlJ8,3281
7
+ gora/go_tui/go.mod,sha256=LKS2kXUljZdD1ui0mD-ICkm9KFSxlydS3FpfEUhLkJY,1719
8
+ gora/go_tui/go.sum,sha256=7XrrBzWw7QNM0L1Lx_igfdkzo3VC_Av7lX_RgZ3yrkY,9414
9
+ gora/go_tui/main.go,sha256=5JE-xDGdktGBf7_bnT-8fg6lVI1fnoEdu5b457DAs8k,69649
10
+ gora/go_tui/main_test.go,sha256=mg9FnSD8BtqVUZ5fQOBN2VV9nVSLjATf75dbRNky2n4,18898
11
+ gora_cli-0.1.2.dist-info/METADATA,sha256=qIfXirY-apOcJXV8PoQZ_0W-FvR08fidxFazyTRf8xo,6576
12
+ gora_cli-0.1.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
13
+ gora_cli-0.1.2.dist-info/entry_points.txt,sha256=KM28YlhjZ6de0vYJ5yiP2JcjjoU4BKNM-5jk4ABWhD0,39
14
+ gora_cli-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ gora = gora.cli:main