@vladar107/claudescope 0.2.0 → 0.4.0
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.
- package/README.md +126 -50
- package/cli.js +6 -6
- package/package.json +7 -3
- package/pricing.default.json +7 -2
- package/server.js +264 -197
- package/web/assets/index-CFGvQhPn.css +1 -0
- package/web/assets/index-Cd1b_MtY.js +199 -0
- package/web/assets/{index-_Et9upO3.js → index-lqhFCxBS.js} +1 -1
- package/web/index.html +17 -2
- package/web/assets/index-D2huyx2f.js +0 -199
- package/web/assets/index-DhAND_LO.css +0 -1
package/README.md
CHANGED
|
@@ -5,61 +5,97 @@
|
|
|
5
5
|
[](https://nodejs.org)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
|
-
*A scope for your
|
|
8
|
+
*A scope for your AI coding-agent sessions.*
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
Claudescope is a **local, read-only** viewer that brings every AI coding-agent
|
|
11
|
+
transcript on your machine into one place — to browse, read, search, and analyze.
|
|
12
|
+
Sessions from every agent that worked in a directory are **merged under one
|
|
13
|
+
project**, each tagged with the agent that produced it. It runs entirely on your
|
|
14
|
+
machine and only ever reads your transcripts.
|
|
13
15
|
|
|
16
|
+
## Supported agents
|
|
17
|
+
|
|
18
|
+
| Agent | Transcripts read from |
|
|
19
|
+
| ------------------------------------------------- | --------------------------------------------- |
|
|
20
|
+
| [Claude Code](https://claude.com/claude-code) | `~/.claude/projects/**/*.jsonl` |
|
|
21
|
+
| [OpenAI Codex](https://openai.com/codex) | `~/.codex/sessions/**/rollout-*.jsonl` |
|
|
22
|
+
| [JetBrains Junie](https://www.jetbrains.com/junie/) | `~/.junie/sessions/session-*/events.jsonl` |
|
|
23
|
+
|
|
24
|
+
Each source is optional — a directory that doesn't exist is simply skipped, so
|
|
25
|
+
Claudescope works whether you use one agent or all three. Adding another is just
|
|
26
|
+
[adding another connector](#how-it-works).
|
|
27
|
+
|
|
28
|
+
## What it can do
|
|
29
|
+
|
|
30
|
+
- **Multi-agent** — Claude Code, Codex, and Junie sessions side by side, each labeled with an **agent badge**. A project that several agents touched shows one card with all its agent tags; drill in and **filter the session list by agent**.
|
|
14
31
|
- **Browse** every session grouped by project — titles, dates, message/tool counts, token totals, cost, git branch, PR links.
|
|
15
|
-
- **Read** a session as a clean threaded conversation: markdown, syntax-highlighted code, collapsible thinking, paired tool calls + results, **syntax-highlighted red/green diffs** for
|
|
16
|
-
- **Review changes** via a **Files changed** tab that aggregates every
|
|
32
|
+
- **Read** a session as a clean threaded conversation: markdown, syntax-highlighted code, collapsible thinking, paired tool calls + results, **syntax-highlighted red/green diffs** for edits, attachments, and sidechain/subagent turns. A built-in **find-in-session** bar (⌘/Ctrl+F) searches the whole transcript — including collapsed thinking, tool, and subagent content — auto-expanding and highlighting matches, with a user/assistant filter.
|
|
33
|
+
- **Review changes** via a **Files changed** tab that aggregates every edit/write in the session by file, with per-file diffs and +/− counts (diffs load lazily per file).
|
|
17
34
|
- **Export / share** a session to Markdown — download or copy it, with an optional toggle to **redact** home-dir paths and likely secrets.
|
|
18
|
-
- **Search** full-text across all sessions (DuckDB BM25), with highlighted snippets that deep-link to the exact message.
|
|
19
|
-
- **Analyze** token usage and cost over time, by project, and by
|
|
35
|
+
- **Search** full-text across all sessions, all agents (DuckDB BM25), with highlighted snippets that deep-link to the exact message.
|
|
36
|
+
- **Analyze** token usage and cost over time, by project, by model, and **by agent** — including cache-hit ratio.
|
|
37
|
+
- **Light & dark themes** — follows your system appearance, with a manual toggle.
|
|
20
38
|
|
|
21
39
|
> **Privacy:** Everything runs locally on `127.0.0.1`. The app **never** writes to
|
|
22
|
-
> `~/.claude
|
|
23
|
-
> index and a copy of the pricing
|
|
24
|
-
>
|
|
25
|
-
> (`claudescope update`); nothing about
|
|
40
|
+
> `~/.claude`, `~/.codex`, or `~/.junie` — all are read-only sources. Its only persistent
|
|
41
|
+
> state lives in `~/.claudescope/` — a DuckDB index and a copy of the pricing
|
|
42
|
+
> file, both safe to delete anytime. The sole outbound request is an optional
|
|
43
|
+
> daily check for a newer published version (`claudescope update`); nothing about
|
|
44
|
+
> your transcripts ever leaves your machine.
|
|
26
45
|
|
|
27
46
|
---
|
|
28
47
|
|
|
29
48
|
## Screenshots
|
|
30
49
|
|
|
31
50
|
> The screenshots below use **synthetic demo data** — every project name, path,
|
|
32
|
-
> and message is fabricated
|
|
33
|
-
>
|
|
51
|
+
> and message is fabricated (`acme-web` is a multi-agent project: Claude Code +
|
|
52
|
+
> Codex). They render light or dark to match your system. Regenerate them with
|
|
53
|
+
> **`npm run screenshots`** (seeds the demo data, boots the app, and captures
|
|
54
|
+
> every view in both themes via Playwright).
|
|
34
55
|
|
|
35
|
-
**Browse** — every project and its sessions at a glance
|
|
36
|
-
|
|
56
|
+
**Browse** — every project and its sessions at a glance, each tagged with the
|
|
57
|
+
agents that worked in it: titles, dates, message & tool counts, token totals,
|
|
58
|
+
cost, git branch, and PR links.
|
|
37
59
|
|
|
38
|
-
|
|
60
|
+
<picture>
|
|
61
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/browse-dark.png">
|
|
62
|
+
<img alt="Browse projects and sessions" src="docs/screenshots/browse-light.png">
|
|
63
|
+
</picture>
|
|
39
64
|
|
|
40
65
|
**Read** — a session as a clean threaded conversation: markdown, collapsible
|
|
41
|
-
thinking, **syntax-highlighted red/green diffs** for
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
an **⤓ Export** (Markdown,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
thinking, **syntax-highlighted red/green diffs** for edits, nested **subagent**
|
|
67
|
+
runs, per-message token chips, and a **find-in-session** bar (⌘/Ctrl+F) that
|
|
68
|
+
auto-expands and highlights matches. The breadcrumb links back to the project's
|
|
69
|
+
session list; **Conversation / Files-changed** tabs and an **⤓ Export** (Markdown,
|
|
70
|
+
optional redaction) sit in the header.
|
|
71
|
+
|
|
72
|
+
<picture>
|
|
73
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/session-dark.png">
|
|
74
|
+
<img alt="Session reader: breadcrumb, tabs, Export, the in-session finder, thinking, and a subagent run" src="docs/screenshots/session-light.png">
|
|
75
|
+
</picture>
|
|
76
|
+
|
|
77
|
+
**Search** — full-text across every session and agent (DuckDB BM25) with
|
|
78
|
+
highlighted snippets and user/assistant filters; each result deep-links to the
|
|
79
|
+
exact message.
|
|
80
|
+
|
|
81
|
+
<picture>
|
|
82
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/search-dark.png">
|
|
83
|
+
<img alt="Full-text search across sessions" src="docs/screenshots/search-light.png">
|
|
84
|
+
</picture>
|
|
85
|
+
|
|
86
|
+
**Analyze** — token & cost analytics over time, by project, by model, and **by
|
|
87
|
+
agent**, with a cache-read breakdown. Click a chart legend to toggle a series.
|
|
88
|
+
|
|
89
|
+
<picture>
|
|
90
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/analytics-dark.png">
|
|
91
|
+
<img alt="Token and cost analytics dashboard" src="docs/screenshots/analytics-light.png">
|
|
92
|
+
</picture>
|
|
57
93
|
|
|
58
94
|
---
|
|
59
95
|
|
|
60
96
|
## Quick start
|
|
61
97
|
|
|
62
|
-
**Prerequisite:** [Node.js](https://nodejs.org) **
|
|
98
|
+
**Prerequisite:** [Node.js](https://nodejs.org) **22 or newer** (`node -v`).
|
|
63
99
|
|
|
64
100
|
### Install (recommended)
|
|
65
101
|
|
|
@@ -78,6 +114,20 @@ Try it without installing:
|
|
|
78
114
|
npx @vladar107/claudescope
|
|
79
115
|
```
|
|
80
116
|
|
|
117
|
+
### Other install methods
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Homebrew (macOS / Linux)
|
|
121
|
+
brew install vladar107/tap/claudescope
|
|
122
|
+
|
|
123
|
+
# Nix (any platform) — run without installing, or add to a profile
|
|
124
|
+
nix run github:vladar107/claudescope
|
|
125
|
+
nix profile install github:vladar107/claudescope
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
All channels wrap the same package; `claudescope update` detects how you
|
|
129
|
+
installed it and points you at the right upgrade command.
|
|
130
|
+
|
|
81
131
|
### Commands
|
|
82
132
|
|
|
83
133
|
```bash
|
|
@@ -116,19 +166,26 @@ All optional — set via environment variables.
|
|
|
116
166
|
| Variable | Default | Description |
|
|
117
167
|
| --------------------- | ---------------------- | ---------------------------------------------------------------------- |
|
|
118
168
|
| `PORT` | `4317` | Port the app listens on (or `--port <n>`). |
|
|
119
|
-
| `CLAUDE_PROJECTS_DIR` | `~/.claude/projects` | Where to read
|
|
169
|
+
| `CLAUDE_PROJECTS_DIR` | `~/.claude/projects` | Where to read Claude Code transcripts from. A leading `~` is expanded. |
|
|
170
|
+
| `CODEX_SESSIONS_DIR` | `~/.codex/sessions` | Where to read OpenAI Codex transcripts from. A leading `~` is expanded.|
|
|
171
|
+
| `JUNIE_SESSIONS_DIR` | `~/.junie/sessions` | Where to read JetBrains Junie transcripts from. A leading `~` is expanded.|
|
|
120
172
|
| `CLAUDESCOPE_HOME` | `~/.claudescope` | Where the app keeps its own state (index, pricing copy, logs, PID). |
|
|
121
173
|
| `REINDEX_INTERVAL_MS` | `15000` | How often to auto-pick-up new/updated sessions. Set `0` to disable. |
|
|
122
174
|
|
|
175
|
+
Each agent source is optional — if a directory doesn't exist it's simply skipped,
|
|
176
|
+
so the app works whether you use one agent or all three.
|
|
177
|
+
|
|
123
178
|
Examples:
|
|
124
179
|
|
|
125
180
|
```bash
|
|
126
181
|
claudescope --port 8080 # custom port
|
|
127
182
|
CLAUDE_PROJECTS_DIR=/path/to/exported/projects claudescope # view someone else's transcripts
|
|
183
|
+
CODEX_SESSIONS_DIR=/path/to/codex/sessions claudescope # point at Codex sessions elsewhere
|
|
184
|
+
JUNIE_SESSIONS_DIR=/path/to/junie/sessions claudescope # point at Junie sessions elsewhere
|
|
128
185
|
claudescope --no-open # don't pop a browser tab
|
|
129
186
|
```
|
|
130
187
|
|
|
131
|
-
The startup banner prints the resolved URL and the
|
|
188
|
+
The startup banner prints the resolved URL and the source directories in use, so
|
|
132
189
|
you can always confirm what it's reading.
|
|
133
190
|
|
|
134
191
|
### Cost methodology
|
|
@@ -150,10 +207,10 @@ just a `SUM` over events; a project/session total is the sum of its events.
|
|
|
150
207
|
Rates live in `~/.claudescope/pricing.json` (seeded on first run from the copy
|
|
151
208
|
shipped with the package; when running from source, `packages/server/pricing.json`).
|
|
152
209
|
A model id resolves in this order:
|
|
153
|
-
exact `models` entry → **family** match (`opus` / `sonnet` / `haiku`
|
|
154
|
-
`default`. The family step means version- or date-suffixed ids (e.g.
|
|
155
|
-
`claude-haiku-4-5-20251001`) still price correctly.
|
|
156
|
-
from Anthropic
|
|
210
|
+
exact `models` entry → **family** match (`opus` / `sonnet` / `haiku` / `gpt`
|
|
211
|
+
substring) → `default`. The family step means version- or date-suffixed ids (e.g.
|
|
212
|
+
`claude-haiku-4-5-20251001`, or a Codex `gpt-5.x-codex` id) still price correctly.
|
|
213
|
+
Shipped rates (USD per 1M tokens, from Anthropic and OpenAI published API pricing):
|
|
157
214
|
|
|
158
215
|
| family / model | input | output | cache write (5m) | cache read |
|
|
159
216
|
| ------------------- | ----- | ------ | ---------------- | ---------- |
|
|
@@ -161,6 +218,9 @@ from Anthropic's published API pricing):
|
|
|
161
218
|
| Opus 4.1 / 4 | $15 | $75 | $18.75 | $1.50 |
|
|
162
219
|
| Sonnet 4.x | $3 | $15 | $3.75 | $0.30 |
|
|
163
220
|
| Haiku 4.5 | $1 | $5 | $1.25 | $0.10 |
|
|
221
|
+
| GPT-5 | $0.63 | $5 | — | $0.13 |
|
|
222
|
+
| GPT-5.4 | $2.50 | $15 | — | $0.50 |
|
|
223
|
+
| GPT-5.5 | $5 | $30 | — | $0.50 |
|
|
164
224
|
| `<synthetic>` | $0 | $0 | $0 | $0 |
|
|
165
225
|
|
|
166
226
|
- Edit `~/.claudescope/pricing.json` to update prices or add models, then re-index
|
|
@@ -169,9 +229,9 @@ from Anthropic's published API pricing):
|
|
|
169
229
|
published pricing page. There's no official pricing *API*, so this is a
|
|
170
230
|
best-effort scrape (it validates what it parses and won't write garbage) —
|
|
171
231
|
review the diff afterwards. Use `--dry-run` to preview without writing.
|
|
172
|
-
- The `opus`/`sonnet`/`haiku` family rules use **current** pricing; the
|
|
173
|
-
Opus 4 / 4.1 ($15/$75)
|
|
174
|
-
to override any specific model.
|
|
232
|
+
- The `opus`/`sonnet`/`haiku`/`gpt` family rules use **current** pricing; the
|
|
233
|
+
deprecated Opus 4 / 4.1 ($15/$75) and specific GPT-5 versions are pinned via
|
|
234
|
+
exact `models` entries. Add an exact entry to override any specific model.
|
|
175
235
|
|
|
176
236
|
> **Caveat:** these are **list-price estimates** — they ignore any discounts,
|
|
177
237
|
> service tier, or batch pricing, and the cache-write rate assumes the 5-minute
|
|
@@ -195,8 +255,16 @@ served from cache (legitimately high for Claude Code, which re-reads cached cont
|
|
|
195
255
|
restart. In an open session, hit **⟳ Refresh** (or ⌘R / Ctrl+R) to pull the
|
|
196
256
|
latest messages in place without losing your scroll position. Each scan is
|
|
197
257
|
near-free when nothing changed; you can also force one with `POST /api/reindex`.
|
|
198
|
-
- **Thinking blocks** appear empty because Claude Code stores only a signature
|
|
199
|
-
not the
|
|
258
|
+
- **Thinking blocks** appear empty because Claude Code stores only a signature
|
|
259
|
+
(and Codex only encrypted reasoning), not the plaintext — the app notes this
|
|
260
|
+
explicitly. (Not a bug.)
|
|
261
|
+
- **Codex sessions have no stored title**, so the title falls back to the first
|
|
262
|
+
user message.
|
|
263
|
+
- **Junie sessions render differently.** Junie records an event-sourced UI stream
|
|
264
|
+
rather than a chat log, so a session reads as tool / terminal / file blocks plus
|
|
265
|
+
a final result — there's no assistant prose or thinking to show. Pasted
|
|
266
|
+
screenshots are surfaced inline. Older Junie sessions don't record a working
|
|
267
|
+
directory and group under an **"(unknown — Junie)"** project.
|
|
200
268
|
|
|
201
269
|
---
|
|
202
270
|
|
|
@@ -215,6 +283,12 @@ and analytics; a small TypeScript parser assembles the threaded view for a singl
|
|
|
215
283
|
session. The index is a derived cache — if it's ever corrupted (e.g. the process
|
|
216
284
|
is killed mid-write) the app discards and rebuilds it automatically.
|
|
217
285
|
|
|
286
|
+
Each agent is a **connector** (`packages/server/src/connectors/`). Claude Code
|
|
287
|
+
JSONL is projected per-row; Codex spreads a session across record types and Junie
|
|
288
|
+
records an event-sourced UI stream, so those connectors normalize a session to
|
|
289
|
+
canonical NDJSON first — after that the indexing, search, cost, and threading
|
|
290
|
+
paths are shared. Adding another agent is adding another connector.
|
|
291
|
+
|
|
218
292
|
---
|
|
219
293
|
|
|
220
294
|
## Development
|
|
@@ -256,8 +330,8 @@ bundles, and publishes. Auth uses npm **Trusted Publishing** (OIDC) — no
|
|
|
256
330
|
|
|
257
331
|
## Security & privacy
|
|
258
332
|
|
|
259
|
-
Claudescope runs entirely on your machine. It treats `~/.claude
|
|
260
|
-
**binds to `127.0.0.1` only**, sends **no telemetry**, and its sole outbound
|
|
333
|
+
Claudescope runs entirely on your machine. It treats `~/.claude`, `~/.codex`, and
|
|
334
|
+
`~/.junie` as **read-only**, **binds to `127.0.0.1` only**, sends **no telemetry**, and its sole outbound
|
|
261
335
|
request is a cached npm-registry version check for the update notice. See
|
|
262
336
|
[`SECURITY.md`](./SECURITY.md) for the full breakdown of filesystem, network,
|
|
263
337
|
shell, and self-update behavior — and how to report a vulnerability.
|
|
@@ -266,10 +340,12 @@ shell, and self-update behavior — and how to report a vulnerability.
|
|
|
266
340
|
|
|
267
341
|
## Troubleshooting
|
|
268
342
|
|
|
269
|
-
- **App is empty / "sessions directory not found"** — `CLAUDE_PROJECTS_DIR
|
|
270
|
-
|
|
343
|
+
- **App is empty / "sessions directory not found"** — none of `CLAUDE_PROJECTS_DIR`,
|
|
344
|
+
`CODEX_SESSIONS_DIR`, or `JUNIE_SESSIONS_DIR` points at real transcripts. Check
|
|
345
|
+
the banner and set them correctly. Any source can be absent; only the present
|
|
346
|
+
ones are indexed.
|
|
271
347
|
- **`Error: listen EADDRINUSE :4317`** — the port is taken; run `claudescope --port <n>`.
|
|
272
|
-
- **Node version errors** — you need Node ≥
|
|
348
|
+
- **Node version errors** — you need Node ≥ 22 (`node -v`).
|
|
273
349
|
- **Stale or wrong data** — delete `~/.claudescope/index.duckdb*` and
|
|
274
350
|
`claudescope restart` to rebuild the index from scratch.
|
|
275
351
|
- **`@duckdb/node-api` install issues** — it ships prebuilt native binaries;
|
package/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __cr } from 'node:module';
|
|
3
3
|
const require = __cr(import.meta.url);
|
|
4
|
-
import{spawn as
|
|
4
|
+
import{spawn as T,spawnSync as b}from"node:child_process";import{existsSync as D,mkdirSync as $,openSync as F,readFileSync as I,realpathSync as J,rmSync as y,writeFileSync as U}from"node:fs";import{dirname as V,join as h}from"node:path";import{createInterface as G}from"node:readline/promises";import{parseArgs as W}from"node:util";import{fileURLToPath as K}from"node:url";import{copyFileSync as ie,existsSync as H,mkdirSync as ae}from"node:fs";import{homedir as l}from"node:os";import{dirname as M,join as s}from"node:path";import{fileURLToPath as B}from"node:url";var v=M(B(import.meta.url));function x(e,o){return e.find(t=>H(t))??o}var E=Number(process.env.PORT??4317),f=s(v,"..");function m(e){return e==="~"?l():e.startsWith("~/")?s(l(),e.slice(2)):e}var w=m(process.env.CLAUDE_PROJECTS_DIR??s(l(),".claude","projects")),ue=m(process.env.CODEX_SESSIONS_DIR??s(l(),".codex","sessions")),de=m(process.env.JUNIE_SESSIONS_DIR??s(l(),".junie","sessions")),fe=process.env.OPEN_BROWSER==="1",me=Number(process.env.REINDEX_INTERVAL_MS??15e3),a=m(process.env.CLAUDESCOPE_HOME??s(l(),".claudescope")),ge=process.env.DUCKDB_PATH??s(a,"index.duckdb"),he=x([s(v,"pricing.default.json"),s(f,"pricing.json")],s(f,"pricing.json")),Se=process.env.PRICING_PATH??s(a,"pricing.json"),ve=process.env.WEB_DIST_DIR??x([s(v,"web"),s(f,"..","web","dist")],s(f,"..","web","dist")),i="0.4.0";var P=V(K(import.meta.url)),X=h(P,"server.js"),p=h(a,"daemon.json"),g=h(a,"daemon.log"),_=h(a,"update-check.json"),u="@vladar107/claudescope",q=24*60*60*1e3;function S(){if(!D(p))return null;try{return JSON.parse(I(p,"utf8"))}catch{return null}}function d(e){try{return process.kill(e,0),!0}catch{return!1}}async function C(e){try{return(await fetch(`http://127.0.0.1:${e}/api/health`,{signal:AbortSignal.timeout(1500)})).ok}catch{return!1}}async function Y(e,o){let t=Date.now()+o;for(;Date.now()<t;){if(await C(e))return!0;process.stdout.write("."),await new Promise(r=>setTimeout(r,500))}return!1}function R(e){let o=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";try{T(o,[e],{stdio:"ignore",detached:!0,shell:process.platform==="win32"}).unref()}catch{}}async function N(e,o){$(a,{recursive:!0});let t=S();if(t&&d(t.pid)&&await C(t.port)){console.log(`\u2713 claudescope is already running \u2192 ${t.url}`),o&&R(t.url);return}t&&!d(t.pid)&&y(p,{force:!0});let r=`http://localhost:${e}`,n=F(g,"a"),c=T(process.execPath,[X],{detached:!0,stdio:["ignore",n,n],env:{...process.env,PORT:String(e),OPEN_BROWSER:"0"}});if(c.unref(),U(p,JSON.stringify({pid:c.pid,port:e,url:r,version:i,startedAt:new Date().toISOString()},null,2)),process.stdout.write("\u203A Starting claudescope"),!await Y(e,2e4)){console.error(`
|
|
5
5
|
\u2717 Server did not become healthy in time. Inspect: claudescope logs`),process.exitCode=1;return}console.log(`
|
|
6
|
-
\u2713 claudescope running \u2192 ${r}`),console.log(` Sessions: ${
|
|
7
|
-
\u2B06 Update available: v${i} \u2192 v${
|
|
6
|
+
\u2713 claudescope running \u2192 ${r}`),console.log(` Sessions: ${w} (read-only)`),o&&R(r),await j(!1)}function O(){let e=S();if(!e||!d(e.pid)){console.log("claudescope is not running."),y(p,{force:!0});return}try{process.kill(e.pid,"SIGTERM")}catch{}y(p,{force:!0}),console.log(`\u2713 Stopped claudescope (pid ${e.pid}).`)}async function z(){let e=S();e&&d(e.pid)&&await C(e.port)?console.log(`\u25CF running ${e.url} (pid ${e.pid}, v${e.version})`):console.log(`\u25CB stopped (installed v${i})`),await j(!0)}function Q(){let e=S();e&&d(e.pid)?R(e.url):console.log("claudescope is not running. Start it with: claudescope start")}function Z(e){if(!D(g)){console.log("No logs yet.");return}e&&process.platform!=="win32"?b("tail",["-f",g],{stdio:"inherit"}):process.stdout.write(I(g,"utf8"))}async function ee(e,o){if(!process.stdin.isTTY)return o;let t=G({input:process.stdin,output:process.stdout});try{let r=(await t.question(`${e} ${o?"[Y/n]":"[y/N]"} `)).trim().toLowerCase();return r?r==="y"||r==="yes":o}finally{t.close()}}function oe(){let e=P;try{e=J(P)}catch{}return e.includes("/nix/store/")?"nix":/[\\/]Cellar[\\/]claudescope[\\/]/.test(e)?"brew":"npm"}async function te(e){let o=await L(!0);if(o&&!k(o,i)){console.log(`\u2713 Already on the latest version (v${i}).`);return}let t=oe();if(t==="brew"){console.log("claudescope was installed via Homebrew."),console.log(" Run: brew upgrade vladar107/tap/claudescope");return}if(t==="nix"){console.log("claudescope was installed via Nix."),console.log(" Run: nix profile upgrade claudescope"),console.log(" (flake users: re-run `nix run --refresh github:vladar107/claudescope`)");return}o||console.log("\u26A0 Could not reach the npm registry to confirm the latest version.");let r=o?`v${i} \u2192 v${o}`:`v${i} \u2192 latest`;if(console.log(`\u203A Will run: npm install -g ${u}@latest (${r})`),!e&&!await ee("Proceed?",!0)){console.log("Aborted.");return}console.log(`\u203A Updating ${u}\u2026`);let n=process.platform==="win32"?"npm.cmd":"npm";if(b(n,["install","-g",`${u}@latest`],{stdio:"inherit"}).status!==0){console.error(`\u2717 Update failed. If you run via npx, just re-run \`npx ${u}\` to get the latest.`),process.exitCode=1;return}O(),console.log("\u2713 Updated. Restarting\u2026"),b("claudescope",["start"],{stdio:"inherit",shell:process.platform==="win32"})}function k(e,o){let t=e.split(".").map(n=>Number.parseInt(n,10)||0),r=o.split(".").map(n=>Number.parseInt(n,10)||0);for(let n=0;n<3;n++){if((t[n]??0)>(r[n]??0))return!0;if((t[n]??0)<(r[n]??0))return!1}return!1}async function L(e){let o=Date.now();if(!e&&D(_))try{let c=JSON.parse(I(_,"utf8"));if(o-c.lastCheck<q)return c.latest}catch{}let t=`https://registry.npmjs.org/${u.replace("/","%2f")}/latest`,r=await fetch(t,{signal:AbortSignal.timeout(2500)});if(!r.ok)return null;let n=await r.json();return n.version?($(a,{recursive:!0}),U(_,JSON.stringify({lastCheck:o,latest:n.version})),n.version):null}async function j(e){try{let o=await L(e);o&&k(o,i)&&console.log(`
|
|
7
|
+
\u2B06 Update available: v${i} \u2192 v${o}. Run: claudescope update`)}catch{}}function A(){console.log(`claudescope v${i} \u2014 local viewer for Claude Code transcripts
|
|
8
8
|
|
|
9
9
|
Usage: claudescope [command] [options]
|
|
10
10
|
|
|
@@ -20,11 +20,11 @@ Commands:
|
|
|
20
20
|
version Print the installed version
|
|
21
21
|
|
|
22
22
|
Options:
|
|
23
|
-
--port <n> Port to listen on (default ${
|
|
23
|
+
--port <n> Port to listen on (default ${E}, or $PORT)
|
|
24
24
|
--no-open Don't open the browser on start
|
|
25
25
|
-y, --yes Skip the confirmation prompt (for \`update\`)
|
|
26
26
|
|
|
27
27
|
State (index, pricing, logs, PID) lives in ${a}
|
|
28
28
|
(override with $CLAUDESCOPE_HOME). Sessions are read from
|
|
29
|
-
${
|
|
30
|
-
`),
|
|
29
|
+
${w} (override with $CLAUDE_PROJECTS_DIR).`)}async function ne(){let{values:e,positionals:o}=W({allowPositionals:!0,options:{port:{type:"string"},follow:{type:"boolean",short:"f"},help:{type:"boolean",short:"h"},version:{type:"boolean",short:"v"},yes:{type:"boolean",short:"y"},"no-open":{type:"boolean"}}}),t=e.port?Number(e.port):E,r=!e["no-open"],n=o[0];switch(n||(n=e.help?"help":e.version?"version":"start"),n){case"start":await N(t,r);break;case"stop":O();break;case"restart":O(),await N(t,r);break;case"status":await z();break;case"open":Q();break;case"logs":Z(!!e.follow);break;case"update":await te(!!e.yes);break;case"version":console.log(i);break;case"help":A();break;default:console.error(`Unknown command: ${n}
|
|
30
|
+
`),A(),process.exitCode=1}}ne().catch(e=>{console.error(e),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vladar107/claudescope",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Local, read-only web app to browse, read, search, and analyze your Claude Code
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Local, read-only web app to browse, read, search, and analyze your AI coding-agent transcripts — Claude Code, OpenAI Codex, and JetBrains Junie.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
8
8
|
"anthropic",
|
|
9
|
+
"codex",
|
|
10
|
+
"openai",
|
|
11
|
+
"junie",
|
|
12
|
+
"jetbrains",
|
|
9
13
|
"transcripts",
|
|
10
14
|
"sessions",
|
|
11
15
|
"viewer",
|
|
@@ -32,7 +36,7 @@
|
|
|
32
36
|
"README.md"
|
|
33
37
|
],
|
|
34
38
|
"engines": {
|
|
35
|
-
"node": ">=
|
|
39
|
+
"node": ">=22"
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
42
|
"@duckdb/node-api": "^1.5.3-r.3"
|
package/pricing.default.json
CHANGED
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
"models": {
|
|
3
3
|
"claude-opus-4-1": { "input": 15, "output": 75, "cacheWrite": 18.75, "cacheRead": 1.5 },
|
|
4
4
|
"claude-opus-4": { "input": 15, "output": 75, "cacheWrite": 18.75, "cacheRead": 1.5 },
|
|
5
|
-
"<synthetic>": { "input": 0, "output": 0, "cacheWrite": 0, "cacheRead": 0 }
|
|
5
|
+
"<synthetic>": { "input": 0, "output": 0, "cacheWrite": 0, "cacheRead": 0 },
|
|
6
|
+
"gpt-5": { "input": 0.625, "output": 5, "cacheWrite": 0, "cacheRead": 0.125 },
|
|
7
|
+
"gpt-5.4": { "input": 2.5, "output": 15, "cacheWrite": 0, "cacheRead": 0.5 },
|
|
8
|
+
"gpt-5.5": { "input": 5, "output": 30, "cacheWrite": 0, "cacheRead": 0.5 },
|
|
9
|
+
"gpt-5.5-pro": { "input": 30, "output": 180, "cacheWrite": 0, "cacheRead": 30 }
|
|
6
10
|
},
|
|
7
11
|
"families": {
|
|
8
12
|
"opus": { "input": 5, "output": 25, "cacheWrite": 6.25, "cacheRead": 0.5 },
|
|
9
13
|
"sonnet": { "input": 3, "output": 15, "cacheWrite": 3.75, "cacheRead": 0.3 },
|
|
10
|
-
"haiku": { "input": 1, "output": 5, "cacheWrite": 1.25, "cacheRead": 0.1 }
|
|
14
|
+
"haiku": { "input": 1, "output": 5, "cacheWrite": 1.25, "cacheRead": 0.1 },
|
|
15
|
+
"gpt": { "input": 2.5, "output": 15, "cacheWrite": 0, "cacheRead": 0.5 }
|
|
11
16
|
},
|
|
12
17
|
"default": { "input": 3, "output": 15, "cacheWrite": 3.75, "cacheRead": 0.3 }
|
|
13
18
|
}
|