Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
ff38d41
refactor(paths): hoist path helpers to tokscale-core and add get_cach…
junhoyeo Apr 26, 2026
a5f5700
feat(cache): consolidate all caches under ~/.config/tokscale/cache wi…
junhoyeo Apr 26, 2026
c5cfa21
feat(tui): render disk cache regardless of age and always background-…
junhoyeo Apr 26, 2026
d346504
feat(cli,settings): add --write-cache flag and light.writeCache setting
junhoyeo Apr 26, 2026
923a13a
docs: document unified cache layout, --write-cache flag, and light.wr…
junhoyeo Apr 26, 2026
f74a3b8
style: auto-fix lint issues [skip ci]
github-actions[bot] Apr 26, 2026
ce07027
fix(cli,paths): refuse light cache write when filters scope it and re…
junhoyeo Apr 26, 2026
6f4d9bb
fix(antigravity): route cache discovery through config dir
junhoyeo Apr 26, 2026
3a72cae
fix(cli): forward light write-cache flags from models
junhoyeo Apr 26, 2026
8971b38
fix(cache): create message-cache lock dir with ensure_cache_dir
junhoyeo Apr 26, 2026
987cd60
test(cli): isolate wrapped and override-cache path fixtures
junhoyeo Apr 26, 2026
5869faa
test(core): make env-mutating cache path tests panic-safe
junhoyeo Apr 26, 2026
e63be89
fix(cli): treat write_light_cache as best-effort so scan failures don…
junhoyeo Apr 26, 2026
b6c823e
fix(clients): make PathRoot::Config match get_config_dir on Windows
junhoyeo Apr 26, 2026
e5679f3
test(cli): pin XDG_CONFIG_HOME in offline test command and seed prici…
junhoyeo Apr 26, 2026
f5bd599
test(core): pin XDG_CONFIG_HOME in cache fallback tests so CI stays h…
junhoyeo Apr 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,19 @@ Tokscaleは設定を`~/.config/tokscale/settings.json`に保存します:
| `autoRefreshMs` | number | `60000` | 自動更新間隔(30000-3600000ms) |
| `nativeTimeoutMs` | number | `300000` | ネイティブサブプロセス処理の最大時間(5000-3600000ms) |
| `defaultClients` | string[] | `[]` | `--client/-c` フラグを渡さない場合に適用されるクライアントフィルター。`--client` と同じ ID を受け付けます(例: `["opencode", "claude", "synthetic"]`)。未知の ID は無視されます。CLI フラグが指定されるとこのリストは完全に無視されます — マージはしません。 |
| `light.writeCache` | boolean | `false` | `true` のとき、`tokscale --light` はレンダリング直後に TUI キャッシュを原子的に上書きします。CLI フラグ `--write-cache` / `--no-write-cache` が実行ごとに優先されます。 |

#### キャッシュディレクトリ構成

再生成可能なキャッシュはすべて `~/.config/tokscale/cache/` 配下に保存されます(`TOKSCALE_CONFIG_DIR` を設定した場合は `${TOKSCALE_CONFIG_DIR}/cache/`)。

- `tui-data-cache.json` — TUI 起動キャッシュ
- `source-message-cache.bin` + `source-message-cache.lock` — ソースメッセージキャッシュとロックファイル
- `pricing-litellm.json` / `pricing-openrouter.json` — 料金キャッシュ
- `opencode-migration.json` — OpenCode 移行記録
- `fonts/`、`images/` — Wrapped アセットキャッシュ

このディレクトリは削除しても安全です。必要になれば Tokscale が再作成し、再生成します。

### 環境変数

Expand All @@ -499,7 +512,7 @@ Tokscaleは設定を`~/.config/tokscale/settings.json`に保存します:
| 変数 | デフォルト | 説明 |
|----------|---------|-------------|
| `TOKSCALE_NATIVE_TIMEOUT_MS` | `300000`(5分) | `nativeTimeoutMs` 設定をオーバーライド |
| `TOKSCALE_CONFIG_DIR` | unset | 設定ディレクトリ(`settings.json``star-cache.json` の保存場所)をオーバーライドします。絶対パス推奨;相対パスはプロセス CWD を基準に解決されます。CI サンドボックスや非デフォルトの場所を固定したい場合に便利です。設定されている場合、tokscale は macOS のレガシーパス(`~/Library/Application Support/tokscale/`)にフォールバックしません。 |
| `TOKSCALE_CONFIG_DIR` | unset | 設定ディレクトリ(`settings.json``star-cache.json`、および `${TOKSCALE_CONFIG_DIR}/cache/` 配下のすべてのキャッシュ保存場所)をオーバーライドします。絶対パス推奨;相対パスはプロセス CWD を基準に解決されます。CI サンドボックスや非デフォルトの場所を固定したい場合に便利です。設定されている場合、tokscale は macOS のレガシーパス(`~/Library/Application Support/tokscale/`)にフォールバックしません。 |

```bash
# 例:非常に大きなデータセット用にタイムアウトを増加
Expand Down Expand Up @@ -900,7 +913,8 @@ AIコーディングツールはクロスプラットフォームの場所にセ

Tokscaleは以下の場所に設定を保存します:
- **TUI設定**: `%APPDATA%\tokscale\settings.json`(プラットフォームのデフォルト。`TOKSCALE_CONFIG_DIR` でオーバーライド可能)
- **キャッシュ**: `%USERPROFILE%\.cache\tokscale\`
- **キャッシュ**: `%APPDATA%\tokscale\cache\`(統合キャッシュルート)
- **レガシーキャッシュパス**: 以前のリリースで使われていた `%USERPROFILE%\.cache\tokscale\` のような分散パスは、新しい場所に再生成可能データが書かれるまで残ることがあります。
- **Cursor認証情報**: `%USERPROFILE%\.config\tokscale\cursor-credentials.json`
- **Tokscaleアカウント認証情報**: `%USERPROFILE%\.config\tokscale\credentials.json`

Expand Down
18 changes: 16 additions & 2 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,19 @@ Tokscale은 설정을 `~/.config/tokscale/settings.json`에 저장합니다:
| `autoRefreshMs` | number | `60000` | 자동 새로고침 간격 (30000-3600000ms) |
| `nativeTimeoutMs` | number | `300000` | 네이티브 서브프로세스 처리 최대 시간 (5000-3600000ms) |
| `defaultClients` | string[] | `[]` | `--client/-c` 플래그를 전달하지 않을 때 적용되는 기본 클라이언트 필터. `--client`와 동일한 ID를 받습니다 (예: `["opencode", "claude", "synthetic"]`). 알 수 없는 ID는 자동으로 무시됩니다. CLI 플래그가 있으면 이 목록은 완전히 무시됩니다 — 병합되지 않습니다. |
| `light.writeCache` | boolean | `false` | `true`이면 `tokscale --light`가 렌더링 직후 TUI 캐시를 원자적으로 덮어씁니다. CLI 플래그 `--write-cache` / `--no-write-cache`가 실행별로 우선합니다. |

#### 캐시 디렉터리 레이아웃

이제 모든 재생성 가능한 캐시는 `~/.config/tokscale/cache/` 아래에 저장됩니다 (`TOKSCALE_CONFIG_DIR`를 설정한 경우 `${TOKSCALE_CONFIG_DIR}/cache/`):

- `tui-data-cache.json` — TUI 시작 캐시
- `source-message-cache.bin` + `source-message-cache.lock` — 소스 메시지 캐시와 락 파일
- `pricing-litellm.json` / `pricing-openrouter.json` — 가격 캐시
- `opencode-migration.json` — OpenCode 마이그레이션 기록
- `fonts/`, `images/` — Wrapped 에셋 캐시

이 디렉터리는 삭제해도 안전합니다. 필요할 때 Tokscale이 다시 생성하고 채웁니다.

### 환경 변수

Expand All @@ -498,7 +511,7 @@ Tokscale은 설정을 `~/.config/tokscale/settings.json`에 저장합니다:
| 변수 | 기본값 | 설명 |
|----------|---------|-------------|
| `TOKSCALE_NATIVE_TIMEOUT_MS` | `300000` (5분) | `nativeTimeoutMs` 설정 오버라이드 |
| `TOKSCALE_CONFIG_DIR` | unset | 설정 디렉토리(`settings.json``star-cache.json` 위치)를 오버라이드합니다. 절대 경로 권장; 상대 경로는 프로세스 CWD 기준으로 해석됩니다. CI 샌드박스나 비기본 위치를 고정할 때 유용합니다. 설정되면 tokscale은 macOS 레거시 경로(`~/Library/Application Support/tokscale/`)로 폴백하지 않습니다. |
| `TOKSCALE_CONFIG_DIR` | unset | 설정 디렉토리(`settings.json`, `star-cache.json`, 그리고 `${TOKSCALE_CONFIG_DIR}/cache/` 아래의 모든 캐시 위치)를 오버라이드합니다. 절대 경로 권장; 상대 경로는 프로세스 CWD 기준으로 해석됩니다. CI 샌드박스나 비기본 위치를 고정할 때 유용합니다. 설정되면 tokscale은 macOS 레거시 경로(`~/Library/Application Support/tokscale/`)로 폴백하지 않습니다. |

```bash
# 예시: 매우 큰 데이터셋에 대한 타임아웃 증가
Expand Down Expand Up @@ -899,7 +912,8 @@ AI 코딩 도구들은 크로스 플랫폼 위치에 세션 데이터를 저장

Tokscale은 다음 위치에 설정을 저장합니다:
- **TUI 설정**: `%APPDATA%\tokscale\settings.json` (플랫폼 기본값. `TOKSCALE_CONFIG_DIR`로 오버라이드 가능)
- **캐시**: `%USERPROFILE%\.cache\tokscale\`
- **캐시**: `%APPDATA%\tokscale\cache\` (통합 캐시 루트)
- **레거시 캐시 경로**: 이전 릴리스의 `%USERPROFILE%\.cache\tokscale\` 같은 분리된 경로가 새 경로로 다시 생성 가능한 데이터가 기록될 때까지 남아 있을 수 있습니다.
- **Cursor 자격 증명**: `%USERPROFILE%\.config\tokscale\cursor-credentials.json`
- **Tokscale 계정 자격 증명**: `%USERPROFILE%\.config\tokscale\credentials.json`

Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,25 @@ Tokscale stores settings in `~/.config/tokscale/settings.json`:
| `autoRefreshMs` | number | `60000` | Auto-refresh interval (30000-3600000ms) |
| `nativeTimeoutMs` | number | `300000` | Maximum time for native subprocess processing (5000-3600000ms) |
| `defaultClients` | string[] | `[]` | Client filter applied when no `--client/-c` flag is passed. Accepts the same ids as `--client` (e.g. `["opencode", "claude", "synthetic"]`). Unknown ids are silently dropped. CLI flags always override this list completely — no merging. |
| `light.writeCache` | boolean | `false` | When true, `tokscale --light` overwrites the TUI cache atomically after rendering. CLI flags `--write-cache` / `--no-write-cache` override per-invocation. |
| `scanner.extraScanPaths` | object | `{}` | Additional per-client scan roots for sessions outside Tokscale's default home-root locations |

Use `scanner.extraScanPaths` for persistent extra roots such as project-level `.codex` directories or imported Gemini/OpenClaw histories. Tokscale merges these paths with the default scan roots on every run and deduplicates overlapping roots by canonical path.

Use `defaultClients` to pin a personal default — for example, set it to `["opencode", "claude"]` if those are the only clients you use, and `tokscale` (with no flags) will scope every report to them automatically. Pass `--client` on the command line to override for a single run.

#### Cache directory layout

All regenerable caches now live under `~/.config/tokscale/cache/` (or `${TOKSCALE_CONFIG_DIR}/cache/` when overridden):

- `tui-data-cache.json` — TUI startup cache
- `source-message-cache.bin` + `source-message-cache.lock` — source-message cache + lock file
- `pricing-litellm.json` / `pricing-openrouter.json` — pricing caches
- `opencode-migration.json` — OpenCode migration record
- `fonts/` and `images/` — Wrapped asset caches

It is safe to delete this directory. Tokscale will recreate and repopulate it on demand.

### Environment Variables

Environment variables override config file values. For CI/CD or one-off use:
Expand All @@ -515,7 +528,7 @@ Environment variables override config file values. For CI/CD or one-off use:
|----------|---------|-------------|
| `TOKSCALE_NATIVE_TIMEOUT_MS` | `300000` (5 min) | Overrides `nativeTimeoutMs` config |
| `TOKSCALE_EXTRA_DIRS` | unset | One-off extra session roots as `client:/abs/path,client:/abs/path` |
| `TOKSCALE_CONFIG_DIR` | unset | Overrides the config directory (where `settings.json` and `star-cache.json` live). Absolute path recommended; relative paths resolve against the process CWD. Useful for CI sandboxes or pinning a non-default location. When set, tokscale will not fall back to the legacy macOS `~/Library/Application Support/tokscale/` path. |
| `TOKSCALE_CONFIG_DIR` | unset | Overrides the config directory (where `settings.json`, `star-cache.json`, and all caches under `${TOKSCALE_CONFIG_DIR}/cache/` live). Absolute path recommended; relative paths resolve against the process CWD. Useful for CI sandboxes or pinning a non-default location. When set, tokscale will not fall back to the legacy macOS `~/Library/Application Support/tokscale/` path. |
Comment thread
junhoyeo marked this conversation as resolved.

```bash
# Example: Increase timeout for very large datasets
Expand Down Expand Up @@ -919,7 +932,8 @@ AI coding tools store their session data in cross-platform locations. Most tools

Tokscale stores its configuration in:
- **TUI settings**: `%APPDATA%\tokscale\settings.json` (platform default; override with `TOKSCALE_CONFIG_DIR`)
- **Cache**: `%USERPROFILE%\.cache\tokscale\`
- **Cache**: `%APPDATA%\tokscale\cache\` (consolidated cache root)
- **Legacy cache paths**: `%USERPROFILE%\.cache\tokscale\` and `%LOCALAPPDATA%\tokscale\cache\` equivalents from older releases may still exist until regenerated data is written to the new path
- **Cursor credentials**: `%USERPROFILE%\.config\tokscale\cursor-credentials.json`
- **Tokscale account credentials**: `%USERPROFILE%\.config\tokscale\credentials.json`

Expand Down
18 changes: 16 additions & 2 deletions README.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,19 @@ Tokscale 将设置存储在 `~/.config/tokscale/settings.json`:
| `autoRefreshMs` | number | `60000` | 自动刷新间隔(30000-3600000ms) |
| `nativeTimeoutMs` | number | `300000` | 原生子进程处理最大时间(5000-3600000ms) |
| `defaultClients` | string[] | `[]` | 未传递 `--client/-c` 选项时应用的客户端筛选。接受与 `--client` 相同的 ID(例如 `["opencode", "claude", "synthetic"]`)。未知 ID 会被静默丢弃。命令行选项会完全覆盖此列表 — 不会合并。 |
| `light.writeCache` | boolean | `false` | 为 `true` 时,`tokscale --light` 会在渲染完成后以原子方式覆盖 TUI 缓存。CLI 标志 `--write-cache` / `--no-write-cache` 会按次运行覆盖该设置。 |

#### 缓存目录布局

现在所有可再生缓存都位于 `~/.config/tokscale/cache/` 下(如果设置了 `TOKSCALE_CONFIG_DIR`,则为 `${TOKSCALE_CONFIG_DIR}/cache/`):

- `tui-data-cache.json` —— TUI 启动缓存
- `source-message-cache.bin` + `source-message-cache.lock` —— 源消息缓存与锁文件
- `pricing-litellm.json` / `pricing-openrouter.json` —— 定价缓存
- `opencode-migration.json` —— OpenCode 迁移记录
- `fonts/`、`images/` —— Wrapped 资源缓存

删除该目录是安全的。Tokscale 会在需要时重新创建并重新生成其中的内容。

### 环境变量

Expand All @@ -499,7 +512,7 @@ Tokscale 将设置存储在 `~/.config/tokscale/settings.json`:
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `TOKSCALE_NATIVE_TIMEOUT_MS` | `300000`(5 分钟) | 覆盖 `nativeTimeoutMs` 配置 |
| `TOKSCALE_CONFIG_DIR` | unset | 覆盖配置目录(`settings.json``star-cache.json` 所在位置)。建议使用绝对路径;相对路径将基于进程 CWD 解析。适用于 CI 沙箱或固定到非默认位置。设置后,tokscale 不会回退到 macOS 旧路径(`~/Library/Application Support/tokscale/`)。 |
| `TOKSCALE_CONFIG_DIR` | unset | 覆盖配置目录(`settings.json``star-cache.json` 以及 `${TOKSCALE_CONFIG_DIR}/cache/` 下所有缓存的存放位置)。建议使用绝对路径;相对路径将基于进程 CWD 解析。适用于 CI 沙箱或固定到非默认位置。设置后,tokscale 不会回退到 macOS 旧路径(`~/Library/Application Support/tokscale/`)。 |

```bash
# 示例:为非常大的数据集增加超时时间
Expand Down Expand Up @@ -900,7 +913,8 @@ AI 编程工具将会话数据存储在跨平台位置。大多数工具在所

Tokscale 将配置存储在:
- **TUI 设置**: `%APPDATA%\tokscale\settings.json`(平台默认值。可用 `TOKSCALE_CONFIG_DIR` 覆盖)
- **缓存**: `%USERPROFILE%\.cache\tokscale\`
- **缓存**: `%APPDATA%\tokscale\cache\`(统一缓存根目录)
- **旧版缓存路径**: 旧版本曾使用 `%USERPROFILE%\.cache\tokscale\` 这类分散路径;在可再生数据写入新路径之前,这些旧路径可能仍然存在。
- **Cursor 凭据**: `%USERPROFILE%\.config\tokscale\cursor-credentials.json`
- **Tokscale 账号凭据**: `%USERPROFILE%\.config\tokscale\credentials.json`

Expand Down
9 changes: 8 additions & 1 deletion crates/tokscale-cli/src/antigravity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ fn home_dir() -> Result<PathBuf> {
}

pub fn get_antigravity_cache_dir() -> Result<PathBuf> {
Ok(home_dir()?.join(".config/tokscale/antigravity-cache"))
// Route through `paths::get_config_dir()` so `TOKSCALE_CONFIG_DIR`
// covers the antigravity sync cache too — without this, an isolated
// CI profile would still leak to the host's
// `~/.config/tokscale/antigravity-cache/`. On macOS and Linux without
// an override the resolved path is byte-identical to the historic
// hardcoded `~/.config/tokscale/antigravity-cache/`, so existing
// users see no path change and no data migration is required.
Ok(crate::paths::get_config_dir().join("antigravity-cache"))
}

pub fn get_antigravity_sessions_dir() -> Result<PathBuf> {
Expand Down
79 changes: 75 additions & 4 deletions crates/tokscale-cli/src/commands/wrapped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,9 @@ async fn ensure_fonts_loaded(client: &reqwest::Client) -> Result<FontSet> {
let regular_path = cache_dir.join(FIGTREE_REGULAR_FILE);
let bold_path = cache_dir.join(FIGTREE_BOLD_FILE);

copy_legacy_wrapped_cache_file("fonts", FIGTREE_REGULAR_FILE, &regular_path);
copy_legacy_wrapped_cache_file("fonts", FIGTREE_BOLD_FILE, &bold_path);

if !regular_path.exists() {
let _ = fetch_to_file(client, FIGTREE_REGULAR_URL, &regular_path).await;
}
Expand Down Expand Up @@ -1142,6 +1145,7 @@ async fn fetch_and_cache_image(
ensure_cache_dir(&cache_dir)?;

let cached_path = cache_dir.join(filename);
copy_legacy_wrapped_cache_file("images", filename, &cached_path);
if !cached_path.exists() {
fetch_to_file(client, url, &cached_path).await?;
}
Expand All @@ -1159,6 +1163,7 @@ async fn fetch_svg_and_convert_to_png(
ensure_cache_dir(&cache_dir)?;

let cached_path = cache_dir.join(filename);
copy_legacy_wrapped_cache_file("images", filename, &cached_path);
if cached_path.exists() {
return Ok(cached_path);
}
Expand Down Expand Up @@ -1231,13 +1236,37 @@ fn load_rgba_image(path: &Path) -> Result<RgbaImage> {
}

fn get_image_cache_dir() -> Result<PathBuf> {
let home = dirs::home_dir().context("Could not determine home directory")?;
Ok(home.join(".cache").join("tokscale").join("images"))
Ok(crate::paths::get_cache_dir().join("images"))
}

fn get_font_cache_dir() -> Result<PathBuf> {
let home = dirs::home_dir().context("Could not determine home directory")?;
Ok(home.join(".cache").join("tokscale").join("fonts"))
Ok(crate::paths::get_cache_dir().join("fonts"))
}

fn legacy_wrapped_cache_file(subdir: &str, filename: &str) -> Option<PathBuf> {
if crate::paths::is_config_dir_overridden() {
return None;
}

crate::paths::legacy_dot_cache_tokscale_dir().map(|dir| dir.join(subdir).join(filename))
}

fn copy_legacy_wrapped_cache_file(subdir: &str, filename: &str, canonical_path: &Path) {
if canonical_path.exists() {
return;
}

let Some(legacy_path) = legacy_wrapped_cache_file(subdir, filename) else {
return;
};
if !legacy_path.exists() {
return;
}

if let Some(parent) = canonical_path.parent() {
let _ = fs::create_dir_all(parent);
}
let _ = fs::copy(legacy_path, canonical_path);
}

fn calculate_intensity(cost: f64, max_cost: f64) -> u8 {
Expand Down Expand Up @@ -1760,6 +1789,18 @@ fn default_clients() -> Vec<String> {
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
use std::env;
use tempfile::TempDir;

fn restore_env_var(key: &str, value: Option<std::ffi::OsString>) {
unsafe {
match value {
Some(value) => env::set_var(key, value),
None => env::remove_var(key),
}
}
}

// ========== format_tokens_short tests ==========

Expand Down Expand Up @@ -1969,6 +2010,36 @@ mod tests {
);
}

#[test]
#[serial]
fn font_cache_copies_from_legacy_path_when_present() {
let temp_home = TempDir::new().unwrap();
let previous_home = env::var_os("HOME");
let previous_override = env::var_os("TOKSCALE_CONFIG_DIR");
let previous_xdg_config = env::var_os("XDG_CONFIG_HOME");
unsafe {
env::set_var("HOME", temp_home.path());
Comment thread
junhoyeo marked this conversation as resolved.
env::remove_var("TOKSCALE_CONFIG_DIR");
env::remove_var("XDG_CONFIG_HOME");
}

let legacy_path = temp_home
.path()
.join(".cache/tokscale/fonts")
.join(FIGTREE_REGULAR_FILE);
fs::create_dir_all(legacy_path.parent().unwrap()).unwrap();
fs::write(&legacy_path, b"legacy-font-bytes").unwrap();

let canonical_path = get_font_cache_dir().unwrap().join(FIGTREE_REGULAR_FILE);
copy_legacy_wrapped_cache_file("fonts", FIGTREE_REGULAR_FILE, &canonical_path);

assert_eq!(fs::read(&canonical_path).unwrap(), b"legacy-font-bytes");

restore_env_var("HOME", previous_home);
restore_env_var("TOKSCALE_CONFIG_DIR", previous_override);
restore_env_var("XDG_CONFIG_HOME", previous_xdg_config);
}

// ========== format_model_name tests ==========

#[test]
Expand Down
Loading
Loading