scrollback 0.1.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.
- scrollback/__init__.py +8 -0
- scrollback/assets/icon-256.png +0 -0
- scrollback/assets/icon.icns +0 -0
- scrollback/cli.py +1139 -0
- scrollback/clipboard.py +34 -0
- scrollback/export.py +293 -0
- scrollback/fts.py +307 -0
- scrollback/highlight.py +128 -0
- scrollback/katexbundle.py +81 -0
- scrollback/launcher_install.py +209 -0
- scrollback/launchers/scrollback.bat +19 -0
- scrollback/launchers/scrollback.command +19 -0
- scrollback/launchers/scrollback.desktop +10 -0
- scrollback/launchers/scrollback.sh +12 -0
- scrollback/mathspan.py +180 -0
- scrollback/minimd.py +205 -0
- scrollback/models.py +135 -0
- scrollback/serialize.py +83 -0
- scrollback/serverconfig.py +66 -0
- scrollback/sources/__init__.py +6 -0
- scrollback/sources/aider.py +244 -0
- scrollback/sources/base.py +117 -0
- scrollback/sources/claudecode.py +631 -0
- scrollback/sources/codex.py +281 -0
- scrollback/sources/opencode.py +357 -0
- scrollback/sources/registry.py +39 -0
- scrollback/store.py +384 -0
- scrollback/termrender.py +170 -0
- scrollback/web/__init__.py +1 -0
- scrollback/web/app.py +359 -0
- scrollback/web/static/app.js +1245 -0
- scrollback/web/static/apple-touch-icon.png +0 -0
- scrollback/web/static/favicon.png +0 -0
- scrollback/web/static/favicon.svg +41 -0
- scrollback/web/static/index.html +75 -0
- scrollback/web/static/style.css +628 -0
- scrollback/web/static/vendor/highlight.min.js +1213 -0
- scrollback/web/static/vendor/hljs-dark.min.css +10 -0
- scrollback/web/static/vendor/hljs-light.min.css +10 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- scrollback/web/static/vendor/katex/katex.min.css +1 -0
- scrollback/web/static/vendor/katex/katex.min.js +1 -0
- scrollback/web/static/vendor/marked.min.js +6 -0
- scrollback/web/static/vendor/purify.min.js +3 -0
- scrollback/webopen.py +96 -0
- scrollback-0.1.0.dist-info/METADATA +391 -0
- scrollback-0.1.0.dist-info/RECORD +69 -0
- scrollback-0.1.0.dist-info/WHEEL +4 -0
- scrollback-0.1.0.dist-info/entry_points.txt +4 -0
- scrollback-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scrollback
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Navigate, search, copy, and export your AI coding-agent sessions (opencode, Claude Code, ...) from one local, read-only tool.
|
|
5
|
+
Project-URL: Homepage, https://github.com/a-attia/scrollback
|
|
6
|
+
Project-URL: Repository, https://github.com/a-attia/scrollback
|
|
7
|
+
Project-URL: Issues, https://github.com/a-attia/scrollback/issues
|
|
8
|
+
Author: Ahmed Attia
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agent,claude-code,cli,local-first,opencode,session-history,transcript
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
28
|
+
Requires-Dist: jinja2>=3.1; extra == 'all'
|
|
29
|
+
Requires-Dist: pywebview>=5.0; extra == 'all'
|
|
30
|
+
Requires-Dist: rich>=13.0; extra == 'all'
|
|
31
|
+
Requires-Dist: uvicorn>=0.29; extra == 'all'
|
|
32
|
+
Provides-Extra: app
|
|
33
|
+
Requires-Dist: fastapi>=0.110; extra == 'app'
|
|
34
|
+
Requires-Dist: jinja2>=3.1; extra == 'app'
|
|
35
|
+
Requires-Dist: pywebview>=5.0; extra == 'app'
|
|
36
|
+
Requires-Dist: uvicorn>=0.29; extra == 'app'
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
39
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: rich>=13.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
43
|
+
Requires-Dist: uvicorn>=0.29; extra == 'dev'
|
|
44
|
+
Provides-Extra: rich
|
|
45
|
+
Requires-Dist: rich>=13.0; extra == 'rich'
|
|
46
|
+
Provides-Extra: screenshots
|
|
47
|
+
Requires-Dist: fastapi>=0.110; extra == 'screenshots'
|
|
48
|
+
Requires-Dist: jinja2>=3.1; extra == 'screenshots'
|
|
49
|
+
Requires-Dist: playwright>=1.40; extra == 'screenshots'
|
|
50
|
+
Requires-Dist: pywebview>=5.0; extra == 'screenshots'
|
|
51
|
+
Requires-Dist: rich>=13.0; extra == 'screenshots'
|
|
52
|
+
Requires-Dist: uvicorn>=0.29; extra == 'screenshots'
|
|
53
|
+
Provides-Extra: web
|
|
54
|
+
Requires-Dist: fastapi>=0.110; extra == 'web'
|
|
55
|
+
Requires-Dist: jinja2>=3.1; extra == 'web'
|
|
56
|
+
Requires-Dist: pywebview>=5.0; extra == 'web'
|
|
57
|
+
Requires-Dist: uvicorn>=0.29; extra == 'web'
|
|
58
|
+
Description-Content-Type: text/markdown
|
|
59
|
+
|
|
60
|
+
# scrollback
|
|
61
|
+
|
|
62
|
+
[](https://github.com/a-attia/scrollback/actions/workflows/ci.yml)
|
|
63
|
+
|
|
64
|
+
Browse, search, copy, and export your AI coding-agent sessions from one
|
|
65
|
+
local, read-only tool. scrollback reads the conversation history that
|
|
66
|
+
agents like **opencode** and **Claude Code** already keep on disk and gives
|
|
67
|
+
you a single, consistent view across them — from a scriptable command line
|
|
68
|
+
or a local web app.
|
|
69
|
+
|
|
70
|
+
Everything is local-first and strictly **read-only**: scrollback never
|
|
71
|
+
modifies, locks for writing, or uploads your data.
|
|
72
|
+
|
|
73
|
+
You can use it two ways. From the **command line**, list, search, and export
|
|
74
|
+
your sessions in a single scriptable tool:
|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+
Or open the **local web app** to read a transcript in full — with rendered
|
|
79
|
+
Markdown, syntax-highlighted code, and typeset LaTeX math:
|
|
80
|
+
|
|
81
|
+

|
|
83
|
+
|
|
84
|
+
Both views read the same on-disk session stores, so you can jump between
|
|
85
|
+
them freely. (The screenshots above use synthetic demo data.)
|
|
86
|
+
|
|
87
|
+
> **For AI agents:** read [`CONTRIBUTING.md`](CONTRIBUTING.md) for the
|
|
88
|
+
> project conventions. This README is for human readers.
|
|
89
|
+
|
|
90
|
+
## Contents
|
|
91
|
+
|
|
92
|
+
- [Why scrollback](#why-scrollback)
|
|
93
|
+
- [Install](#install)
|
|
94
|
+
- [Quick start](#quick-start)
|
|
95
|
+
- [The command line](#the-command-line)
|
|
96
|
+
- [The web app](#the-web-app)
|
|
97
|
+
- [Running it as an app](#running-it-as-an-app)
|
|
98
|
+
- [Fast search (optional index)](#fast-search-optional-index)
|
|
99
|
+
- [Supported sources](#supported-sources)
|
|
100
|
+
- [Configuration](#configuration)
|
|
101
|
+
- [Safety](#safety)
|
|
102
|
+
- [Development](#development)
|
|
103
|
+
- [License](#license)
|
|
104
|
+
|
|
105
|
+
## Why scrollback
|
|
106
|
+
|
|
107
|
+
AI coding agents persist rich session data locally, but each in its own
|
|
108
|
+
format and with no convenient way to browse that history or take it with
|
|
109
|
+
you. scrollback fills that gap with four things:
|
|
110
|
+
|
|
111
|
+
- **See** any past conversation as a readable transcript.
|
|
112
|
+
- **Search** across every session by keyword (title or full text).
|
|
113
|
+
- **Export** a session to Markdown, JSON, HTML, or plain text.
|
|
114
|
+
- **Copy** a message or a whole session straight to your clipboard.
|
|
115
|
+
|
|
116
|
+
Its niche among similar tools: pure Python, works equally from the
|
|
117
|
+
**CLI and a web UI**, reads **multiple agents** directly from their live
|
|
118
|
+
on-disk stores (no sync step, no plugins, no upload), and treats
|
|
119
|
+
**export and copy** as first-class.
|
|
120
|
+
|
|
121
|
+
## Install
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install "scrollback[all]" # CLI + web app + native window + colour
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Requires Python 3.10+. The bare CLI has **no runtime dependencies**
|
|
128
|
+
(standard library only); optional features come from extras:
|
|
129
|
+
|
|
130
|
+
- `"scrollback[web]"` — the local web app (FastAPI, uvicorn) and the native
|
|
131
|
+
app window (pywebview).
|
|
132
|
+
- `"scrollback[rich]"` — coloured terminal output.
|
|
133
|
+
- `"scrollback[all]"` — everything a user might want at runtime (`web` + `rich`).
|
|
134
|
+
|
|
135
|
+
If you'd rather keep it isolated from your system Python (recommended, but
|
|
136
|
+
optional), [`pipx`](https://pipx.pypa.io) installs it in its own environment
|
|
137
|
+
and still puts the `scrollback` command on your PATH:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pipx install "scrollback[all]"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Either way, plain `pip` works the same; `pipx` is just a convenience.
|
|
144
|
+
|
|
145
|
+
From a local clone (for development), use an editable install with the dev
|
|
146
|
+
extra:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install -e ".[web,dev]"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Quick start
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
scrollback doctor # what was detected on this machine?
|
|
156
|
+
scrollback list # recent sessions, newest first
|
|
157
|
+
scrollback show latest # print the most recent transcript
|
|
158
|
+
scrollback web # open the browser UI
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`scrollback doctor` is the best first command: it reports which agents
|
|
162
|
+
were found, how many sessions each has, and which optional features are
|
|
163
|
+
available.
|
|
164
|
+
|
|
165
|
+
## The command line
|
|
166
|
+
|
|
167
|
+
The CLI is organised around a few verbs. Commands that operate on a single
|
|
168
|
+
session accept a **selector**: a full id, a unique prefix, a
|
|
169
|
+
source-qualified id (`opencode:ses_0eae9810`), or the keyword `latest`.
|
|
170
|
+
|
|
171
|
+
### Listing and viewing
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
scrollback list --source opencode -n 10 # one source, 10 rows
|
|
175
|
+
scrollback list --dir myproject # filter by directory substring
|
|
176
|
+
scrollback list -q "refactor" # filter by title substring
|
|
177
|
+
scrollback list --since 2026-06-01 --until 2026-06-30 # date range
|
|
178
|
+
scrollback list --usage # add cost + token (in/out) columns
|
|
179
|
+
scrollback list -n 20 --page 2 # pagination (page size = --limit)
|
|
180
|
+
|
|
181
|
+
scrollback show latest --reasoning # include the model's thinking
|
|
182
|
+
scrollback show <selector> --no-tools # hide tool calls and output
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
By default, subagent sessions (for example opencode `@explore` subagents)
|
|
186
|
+
are **folded** under their parent; pass `--no-fold` to list them flat.
|
|
187
|
+
Output is coloured when the `rich` extra is installed and the output is a
|
|
188
|
+
terminal; piping, or `--plain`, falls back to plain text.
|
|
189
|
+
|
|
190
|
+
### Searching
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
scrollback search "merge conflict" # full-text across all sessions
|
|
194
|
+
scrollback search "ssh" --source opencode --json
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Search scans message text across sessions. On a large history you can make
|
|
198
|
+
it near-instant with an [optional index](#fast-search-optional-index).
|
|
199
|
+
|
|
200
|
+
### Exporting and copying
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
scrollback export latest -f markdown -o session.md
|
|
204
|
+
scrollback export <selector> -f html -o session.html
|
|
205
|
+
scrollback export <selector> -f html --math rendered -o session.html
|
|
206
|
+
scrollback export <selector> -f json # to stdout
|
|
207
|
+
scrollback copy latest -f markdown # render and copy to the clipboard
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The formats are `markdown` (`md`), `json`, `html`, and `text` (`txt`).
|
|
211
|
+
Markdown, HTML, and text honour `--reasoning` (include the model's
|
|
212
|
+
thinking) and `--no-tools` (omit tool calls and their output); JSON is a
|
|
213
|
+
faithful structured dump with bulky raw blobs stripped for readability.
|
|
214
|
+
Exported HTML and Markdown render the assistant's Markdown with syntax-
|
|
215
|
+
highlighted code, and the HTML is a self-contained file that prints well.
|
|
216
|
+
|
|
217
|
+
Mathematical notation in delimited LaTeX (`$...$`, `$$...$$`, `\(...\)`,
|
|
218
|
+
`\[...\]`) is preserved verbatim in every format, never mangled by the
|
|
219
|
+
Markdown pass. `--math` controls how the HTML export treats it: `raw`
|
|
220
|
+
(verbatim source, the default), `latex` (verbatim, marked never-to-typeset
|
|
221
|
+
— best for pasting into a paper), or `rendered` (typeset with KaTeX, which
|
|
222
|
+
is embedded into the file with its fonts so the equations render offline).
|
|
223
|
+
In the web app the same choice is a `math:` toggle in the transcript header.
|
|
224
|
+
|
|
225
|
+
### Stats and resume
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
scrollback stats # totals, by-source + top projects
|
|
229
|
+
scrollback resume latest # print the native resume command
|
|
230
|
+
scrollback resume <selector> --copy # ...and copy it to the clipboard
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`stats` aggregates session counts, message/token/cost totals, and your
|
|
234
|
+
busiest projects. `resume` prints the command to continue a session in its
|
|
235
|
+
own agent (for example `opencode --session <id>` or `claude --resume <id>`),
|
|
236
|
+
with a `cd` into the session's project directory.
|
|
237
|
+
|
|
238
|
+
## The web app
|
|
239
|
+
|
|
240
|
+
`scrollback web` starts a local, read-only browser UI — FastAPI plus a
|
|
241
|
+
small vanilla-JavaScript frontend with no build step — bound to
|
|
242
|
+
`127.0.0.1`. Open it with `scrollback web` (a browser tab),
|
|
243
|
+
`scrollback web --window` (a standalone browser window), or
|
|
244
|
+
`scrollback web --app` (a native desktop window; see
|
|
245
|
+
[Running it as an app](#running-it-as-an-app)).
|
|
246
|
+
|
|
247
|
+
What it offers:
|
|
248
|
+
|
|
249
|
+
- A **session list** with source-filter chips, date filters, and a
|
|
250
|
+
**home** button to reset everything; it loads incrementally as you
|
|
251
|
+
scroll.
|
|
252
|
+
- An explicit **search scope** toggle — search session **titles**, message
|
|
253
|
+
**contents**, or both at once (combined results are grouped).
|
|
254
|
+
- **Subagents** collapsed under their parent, expandable on demand
|
|
255
|
+
(including Claude Code's nested sidechain transcripts).
|
|
256
|
+
- A **transcript reader** with a **collapsible** frozen header (auto-
|
|
257
|
+
collapses as you scroll; toggle with `h`) over a scrolling message body,
|
|
258
|
+
**Markdown rendering with syntax highlighting**, **LaTeX math** (source /
|
|
259
|
+
paste-ready / typeset), in-transcript find, show-reasoning / show-tools
|
|
260
|
+
toggles, and per-message and per-session copy.
|
|
261
|
+
- **Export** (Markdown / HTML / JSON), **print**, a **light/dark theme**,
|
|
262
|
+
and **keyboard navigation** (`/` search, `j`/`k` move, `Enter` open,
|
|
263
|
+
`f` find, `h` collapse header, `Esc` blur).
|
|
264
|
+
|
|
265
|
+
Large transcripts open instantly because the app loads a session's header
|
|
266
|
+
first and then pages messages in as you scroll, rather than transferring an
|
|
267
|
+
entire multi-megabyte transcript at once. Deep links work too: the open
|
|
268
|
+
session is reflected in the URL hash (`#opencode/<id>`), and `?q=<text>`
|
|
269
|
+
pre-fills a content search.
|
|
270
|
+
|
|
271
|
+
## Running it as an app
|
|
272
|
+
|
|
273
|
+
You don't have to type a command every time. After `pip install ".[web]"`:
|
|
274
|
+
|
|
275
|
+
- **Short commands** are on your `PATH`: `scrollback-web` (a browser tab)
|
|
276
|
+
and `scrollback-app` (a native window).
|
|
277
|
+
- **A double-clickable launcher** is one command away:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
scrollback install-launcher # both: Desktop launcher + .app (macOS)
|
|
281
|
+
scrollback install-launcher --desktop # only the Desktop launcher
|
|
282
|
+
scrollback install-launcher --app-bundle # only the ~/Applications/.app (macOS)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
With no flags it installs everything for your OS; the two flags let you
|
|
286
|
+
pick just one. The Desktop launcher is `scrollback.command` on macOS,
|
|
287
|
+
`scrollback.bat` on Windows, and an application-menu entry plus
|
|
288
|
+
`scrollback.sh` on Linux. `--app-bundle` builds an
|
|
289
|
+
`~/Applications/scrollback.app` on macOS and falls back to the Desktop
|
|
290
|
+
launcher on other platforms (where there is no `.app`). Use `--dest <dir>`
|
|
291
|
+
to place artifacts elsewhere.
|
|
292
|
+
|
|
293
|
+
The launchers open a **native window** via pywebview when it is available:
|
|
294
|
+
no browser tab, no terminal, and **closing the window stops the server and
|
|
295
|
+
frees the port**. On a system where pywebview cannot run (for example a
|
|
296
|
+
headless Linux box without a GTK/Qt WebKit backend), scrollback falls back
|
|
297
|
+
to a standalone browser window that auto-stops the server shortly after the
|
|
298
|
+
window closes. All of this behaviour is decided in Python, so the launcher
|
|
299
|
+
scripts stay free of OS-specific assumptions and ship inside the package
|
|
300
|
+
for `pip install` users.
|
|
301
|
+
|
|
302
|
+
## Fast search (optional index)
|
|
303
|
+
|
|
304
|
+
By default, search is a lexical scan over your live data: zero setup,
|
|
305
|
+
always correct, but its cost grows with the size of your history. For a
|
|
306
|
+
large corpus, build an optional full-text index:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
scrollback index # one-time build; re-run to update (incremental)
|
|
310
|
+
scrollback index --stats # show what's indexed
|
|
311
|
+
scrollback index --clear # delete the index
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The index is a separate SQLite FTS5 database at
|
|
315
|
+
`~/.cache/scrollback/index.db` (override with `SCROLLBACK_INDEX`). It is
|
|
316
|
+
derived and disposable: your source data is never modified, and deleting
|
|
317
|
+
the index simply reverts to the lexical scan. Re-running `index` only
|
|
318
|
+
re-processes new or changed sessions and prunes deleted ones; the web app
|
|
319
|
+
also refreshes it in the background on startup when it is stale.
|
|
320
|
+
|
|
321
|
+
Once built, both the CLI and the web app use it automatically, turning a
|
|
322
|
+
multi-second query into a few milliseconds. If your Python's SQLite was
|
|
323
|
+
built without FTS5, `index` says so and search keeps working without it.
|
|
324
|
+
|
|
325
|
+
## Supported sources
|
|
326
|
+
|
|
327
|
+
| Source | Reads | Default location |
|
|
328
|
+
|:-------------|:-----------------------------------------------------------|:---------------------------------------|
|
|
329
|
+
| `opencode` | SQLite (`session` / `message` / `part`), read-only | `~/.local/share/opencode/opencode.db` |
|
|
330
|
+
| `claudecode` | per-project JSONL transcripts + nested subagent sidechains | `~/.claude/projects/` |
|
|
331
|
+
| `codex` | per-session `rollout-*.jsonl` rollouts | `~/.codex/sessions/` |
|
|
332
|
+
| `aider` | per-project `.aider.chat.history.md` Markdown logs | set `SCROLLBACK_AIDER_DIRS` to opt in |
|
|
333
|
+
|
|
334
|
+
More agents (Gemini CLI, Zed, VS Code Copilot Chat, GitHub Copilot CLI) are
|
|
335
|
+
researched and queued — see [`ROADMAP.md`](ROADMAP.md).
|
|
336
|
+
|
|
337
|
+
Adding another agent is a small, self-contained change: implement the
|
|
338
|
+
`Source` interface in `src/scrollback/sources/base.py` and register it in
|
|
339
|
+
`src/scrollback/sources/registry.py`. The CLI, search, export, web app,
|
|
340
|
+
and index all work against the common model automatically — see the
|
|
341
|
+
opencode (SQLite) and Claude Code (JSONL) adapters as references, and
|
|
342
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md) for the conventions.
|
|
343
|
+
|
|
344
|
+
## Configuration
|
|
345
|
+
|
|
346
|
+
scrollback reads each agent's data from its default location, but you can
|
|
347
|
+
point it elsewhere, and you can control how the web server binds:
|
|
348
|
+
|
|
349
|
+
| Variable | Purpose |
|
|
350
|
+
|:--------------------------|:------------------------------------------------------------|
|
|
351
|
+
| `SCROLLBACK_OPENCODE_DB` | path to `opencode.db` |
|
|
352
|
+
| `SCROLLBACK_CLAUDE_DIR` | path to `~/.claude` or `~/.claude/projects` |
|
|
353
|
+
| `SCROLLBACK_CODEX_DIR` | path to `~/.codex` or `~/.codex/sessions` |
|
|
354
|
+
| `SCROLLBACK_AIDER_DIRS` | colon-separated dirs to scan for Aider history (opt-in) |
|
|
355
|
+
| `SCROLLBACK_PORT` | web server port (default `8765`; or use `--port`) |
|
|
356
|
+
| `SCROLLBACK_HOST` | web server bind host (default `127.0.0.1`; or use `--host`) |
|
|
357
|
+
| `SCROLLBACK_INDEX` | path to the search index database |
|
|
358
|
+
|
|
359
|
+
The web server defaults to `127.0.0.1`. If the chosen port is busy,
|
|
360
|
+
scrollback automatically picks the next free one (`--strict-port` fails
|
|
361
|
+
instead). Binding to a non-loopback host prints a warning, since the
|
|
362
|
+
read-only API is unauthenticated.
|
|
363
|
+
|
|
364
|
+
## Safety
|
|
365
|
+
|
|
366
|
+
scrollback is read-only by design, and the design is enforced:
|
|
367
|
+
|
|
368
|
+
- The opencode SQLite database is opened with `mode=ro` — it is never
|
|
369
|
+
created or written, and reads are safe against a live write-ahead log.
|
|
370
|
+
- Claude Code JSONL files are read as read-only.
|
|
371
|
+
- A test asserts the opencode database's modification time is unchanged
|
|
372
|
+
across reads (`tests/test_sources_live.py`).
|
|
373
|
+
- The web app binds to localhost, rejects unexpected `Host` headers (a
|
|
374
|
+
DNS-rebinding guard), and sanitizes rendered transcript content.
|
|
375
|
+
|
|
376
|
+
## Development
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
pip install -e ".[web,dev]"
|
|
380
|
+
pytest -q # tests
|
|
381
|
+
ruff check src tests # lint
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for project conventions (the
|
|
385
|
+
read-only invariant, stdlib-first dependencies, platform-agnostic code,
|
|
386
|
+
and how to add a new agent source) and [`CHANGELOG.md`](CHANGELOG.md) for
|
|
387
|
+
what has landed so far.
|
|
388
|
+
|
|
389
|
+
## License
|
|
390
|
+
|
|
391
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
scrollback/__init__.py,sha256=Yohi9u5i1HXp4Jh8elf4T4B8xknbIvLZNg2ub4EsXTk,316
|
|
2
|
+
scrollback/cli.py,sha256=hWxTvwlcCqUnF1wIkJ9IIxYvZ5V4n2qFV33T9w9Tnvo,41748
|
|
3
|
+
scrollback/clipboard.py,sha256=FMpTsRsrGVbXBbdJRyo1cAethcB6ZRs4OAJCagkNvqE,1005
|
|
4
|
+
scrollback/export.py,sha256=MfXG2QDjNNTqzk9xz2Hvlm733oaS-6zsyTbLdZ4pPCw,11131
|
|
5
|
+
scrollback/fts.py,sha256=h9NsQpOqOIndyDsMHeBtx4aFNfbbqwRUVInxbIWgyfE,11442
|
|
6
|
+
scrollback/highlight.py,sha256=P3ocx_6Q_sBapWfq_3cjclq44hQK3RY8kfeJNrKHDxQ,4859
|
|
7
|
+
scrollback/katexbundle.py,sha256=2hAPxJ4SpJ9AxtREHzmqKCa4kSL_Kjrrp-7ciSnUnVM,2888
|
|
8
|
+
scrollback/launcher_install.py,sha256=iI2lACmCMhWLoiggXTV1qQBskjvyctEUC5HfOoxIZjA,8367
|
|
9
|
+
scrollback/mathspan.py,sha256=sMU8LAm513Rq2BlVD9Ax4ybjNMk_YIPGA2UauXKLVs8,6821
|
|
10
|
+
scrollback/minimd.py,sha256=YqzP7-ZCZP1YV7eYak6n6CoSF-I5f77WNjKe3xJAets,7600
|
|
11
|
+
scrollback/models.py,sha256=9H6YbHRN-27Fa_16eAqhYaSjN1hYAT81bv02rDOC1fY,4277
|
|
12
|
+
scrollback/serialize.py,sha256=apzTWeXqVNhgIYLYC_5UZQRoURZ0EnQe1E-D34B-BZQ,2271
|
|
13
|
+
scrollback/serverconfig.py,sha256=TAH9bscAI82ksCMOlVQDybxfg8OPEYGU4-GZqMGKftc,2011
|
|
14
|
+
scrollback/store.py,sha256=QZt7LCsAkA9MTHOEGhrD8FnMNATo1cw5cKFxQTfcTI8,14424
|
|
15
|
+
scrollback/termrender.py,sha256=WXw7DdG2fH3T3GVvnC5rCVCH0XU588hdMYmEh3DZapA,5869
|
|
16
|
+
scrollback/webopen.py,sha256=979_OUxxFIxhaSgytB0Xle6AawGdBEMwa2ckOQVahcA,3573
|
|
17
|
+
scrollback/assets/icon-256.png,sha256=WVskWeWUXhwbqiFIKm3MVGxTwP5RRae9hKKZCvWmyX0,6397
|
|
18
|
+
scrollback/assets/icon.icns,sha256=MDGD1hXJXeIPRuH5HOmeVe91lsuiqe73o2rPvTWJmJk,114460
|
|
19
|
+
scrollback/launchers/scrollback.bat,sha256=It_rJ0e8kobuHz25mjKyOh274DGwIgjiT1hDSkaoBzU,420
|
|
20
|
+
scrollback/launchers/scrollback.command,sha256=SoP_XqFggHZ0sts-JuYZuaARxRJ4eben8-k4-PRGm4s,693
|
|
21
|
+
scrollback/launchers/scrollback.desktop,sha256=bfiUx2FgX4K48yT5DxgKHVCkfGZ0bg-51kNQihD3PIA,363
|
|
22
|
+
scrollback/launchers/scrollback.sh,sha256=LqBTKXgTbpfKuEdPrCxu7hqtdIqw-PaD3tpmT-Nmq3o,435
|
|
23
|
+
scrollback/sources/__init__.py,sha256=pyiM6nABWX56t8dxM0tA53q4_dqjSWySkbHMfEovECQ,263
|
|
24
|
+
scrollback/sources/aider.py,sha256=Xzd0cg-IxqBrpNktvCG-89Aze8btGxHYcK6fiQQS5zo,8712
|
|
25
|
+
scrollback/sources/base.py,sha256=pb2n4pIr-tzzERDiLopUFNoAw5mHNgbq2gmJbRpOafE,4064
|
|
26
|
+
scrollback/sources/claudecode.py,sha256=gxe3PQaakbsxXZVeaqQEcSydEffUx11I-k0VEwjAN1E,23050
|
|
27
|
+
scrollback/sources/codex.py,sha256=CTMOGX3b1W30wTniPNz4wielyqPEYTGrX9CBubRoz-M,9647
|
|
28
|
+
scrollback/sources/opencode.py,sha256=T5CDQgcWVo1mfS45JzMy0RJifezV5sYqa8GxlSqHMh4,12143
|
|
29
|
+
scrollback/sources/registry.py,sha256=qT7dVs057mZ1uc19MfE-pLCRvS2lKObo3FIHA-F6LHU,1033
|
|
30
|
+
scrollback/web/__init__.py,sha256=Fc0H7B-igSBI1bpUe9N12VRVJegzqWTtyjT2kpFvcg8,65
|
|
31
|
+
scrollback/web/app.py,sha256=NAZY_VwZrHmeVCYrFGYZiB_jMkusrx-3o2B2Q_osvpw,14062
|
|
32
|
+
scrollback/web/static/app.js,sha256=jBRye8hgeboOjqIKenCISNquQZtc-5DtosYkbFPKZMY,46470
|
|
33
|
+
scrollback/web/static/apple-touch-icon.png,sha256=Ca1lF0cMSjJfp7WI207Cvso8HNmue1X-CTNx4KSM8GQ,4748
|
|
34
|
+
scrollback/web/static/favicon.png,sha256=ELp1K-ZQazVPE4bEHOj15FFekKWPtxsn9Sq_yE_APOg,879
|
|
35
|
+
scrollback/web/static/favicon.svg,sha256=ztGZv1Umu7C4Fo3aGqIjrfgKAXu7xN8SOk-1g5v5OgA,1782
|
|
36
|
+
scrollback/web/static/index.html,sha256=zfNoE9dc-dpqfdjt_VGyMY31jttghkY3I2VOKzi4DJw,3080
|
|
37
|
+
scrollback/web/static/style.css,sha256=GT_M7MRsRYhY9ukQr15I1Q-W38jgGoCAE-Tlaf4wl6c,21509
|
|
38
|
+
scrollback/web/static/vendor/highlight.min.js,sha256=g3pvpbDHNrUrveKythkPMF2j_J7UFoHbUyFQcFe1yEY,121727
|
|
39
|
+
scrollback/web/static/vendor/hljs-dark.min.css,sha256=nyCNAiECsdDHrr_s2OQsp5l9XeY2ZJ0rMepjCT2AkBk,1315
|
|
40
|
+
scrollback/web/static/vendor/hljs-light.min.css,sha256=Oppd74ucMR5a5Dq96FxjEzGF7tTw2fZ_6ksAqDCM8GY,1309
|
|
41
|
+
scrollback/web/static/vendor/marked.min.js,sha256=Ffq85bZYmLMrA_XtJen4kacprUwNbYdxEKd0SqhHqJQ,35479
|
|
42
|
+
scrollback/web/static/vendor/purify.min.js,sha256=ZAdXaZOlqhMD6vn--5Xlz8HAyAZFvTcX22cXJ-a1W5E,21531
|
|
43
|
+
scrollback/web/static/vendor/katex/katex.min.css,sha256=cXvJrnhTth8PdkVd3fDs1PUnp4P0LeKsJGhImcHEYlg,23335
|
|
44
|
+
scrollback/web/static/vendor/katex/katex.min.js,sha256=5r_l3uvUx8zScgVbq2O9OrLHO5B7bmoi01J0CoE4H9Q,275414
|
|
45
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_AMS-Regular.woff2,sha256=DN04fJWQoan5eUVgAi27WWVKfYbxh6oMgUla1C06cwg,28076
|
|
46
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2,sha256=3ncB5Czx9M8LdmwD-yeXcgfu4vT9XXb6ghiEBtpD6kw,6912
|
|
47
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2,sha256=XVPnCtYHwjUhYt7J4JI_tU7Nr6zL9gTNjc99APrLmJs,6908
|
|
48
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2,sha256=dERO_Vk8AF4_RXO0RSRwTArwqTf-kRzKnpQGjQ0UDT8,11348
|
|
49
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2,sha256=UYFNJw0G_wJV26B5mZT6TYyE0R8JlR1HWV9Kux82Atw,11316
|
|
50
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Main-Bold.woff2,sha256=D2DRuJeTjskYyM4HMJJBG6-UOPZzlGVpP_GLD50gsCE,25324
|
|
51
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2,sha256=mc1Co8By2Rjy9EmEqAfPeqFuE1Rf0IdfwHxsZfmecVs,16780
|
|
52
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Main-Italic.woff2,sha256=l0ecpszpBqvJYeyslvql-couYbjnZw1HWCa83umnwmc,16988
|
|
53
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Main-Regular.woff2,sha256=wjQs2Lhp4BdSqTIdwXIT_EDU0Ex5aIwdQ_LPMWq9eGY,26272
|
|
54
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2,sha256=3Ec0TbtstbZVyEYNVh9N9fUBuQyAStPGzsZf4yI1GrE,16400
|
|
55
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Math-Italic.woff2,sha256=evWMXsjxMqLd3pAnxteBTezOTTuCKhEZKkKiDi6XMmQ,16440
|
|
56
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2,sha256=6ZrlEUS_EjLvzBv-Wt02JixoZrD6qyT6dXQOG5hXemI,12216
|
|
57
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2,sha256=ALJqyCXiCVBWOW4FU7isJtP4rRWMOCbii0xFs4XEcUo,12028
|
|
58
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2,sha256=aOjHPvQq_TzOxYvw-6MCzORIk45_wCCl4x-KlS7uE0I,10344
|
|
59
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Script-Regular.woff2,sha256=A21OlRSbaf-bzAzVV3Hv6yX_o5Ryk-aazXjVrDKMaEs,9644
|
|
60
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Size1-Regular.woff2,sha256=a0fEAWa22-IaXfyncYQT8hR_0jmb4bpgXYrTnO3yXf4,5468
|
|
61
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Size2-Regular.woff2,sha256=0ExUIZ-ersbU1P1C37KHhZdaR5TWsvxx5Wa5zW24Qt0,5208
|
|
62
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Size3-Regular.woff2,sha256=c9WRJxsWBJYMsQu5D-4CFnCvcpcBfg6YSAszLRH1GZU,3624
|
|
63
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Size4-Regular.woff2,sha256=pK99QURAocF5CCXPtwDPnPQ7DyxLBPDrxSMBGtmFPsA,4928
|
|
64
|
+
scrollback/web/static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2,sha256=cdUX1ngneHz6vfGGkUzDNY7aU543kxlB8rL9SiH2jAs,13568
|
|
65
|
+
scrollback-0.1.0.dist-info/METADATA,sha256=OsVo669oyfwE4eZa8x-bWoB0DH9n07LGwVqgfb86MdQ,17276
|
|
66
|
+
scrollback-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
67
|
+
scrollback-0.1.0.dist-info/entry_points.txt,sha256=8dKwY4-SwjADuMfKRzwPNHrOsw0GnARmMgu_6Fdw7po,133
|
|
68
|
+
scrollback-0.1.0.dist-info/licenses/LICENSE,sha256=SNaLOpUWPjwl91fMGbI5KhB0aJo-hYkzZjyuFaqKlUI,1068
|
|
69
|
+
scrollback-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ahmed Attia
|
|
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.
|