Skip to content

feat: add Codebuff local session support - #454

Merged
junhoyeo merged 4 commits into
junhoyeo:mainfrom
anandghegde:feat/codebuff-sessions
Apr 26, 2026
Merged

feat: add Codebuff local session support#454
junhoyeo merged 4 commits into
junhoyeo:mainfrom
anandghegde:feat/codebuff-sessions

Conversation

@anandghegde

@anandghegde anandghegde commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Codebuff (formerly Manicode) as a new first-class tokscale client, alongside Amp / Droid / Pi / Hermes / Mux / Kimi / Qwen / Kilo / KiloCode / RooCode / Crush.

Data Source

Codebuff persists per-chat history at:

$CODEBUFF_DATA_DIR/                      # default: ~/.config/manicode
  projects/<project>/
    chats/<chatId>/
      chat-messages.json                  # serialized ChatMessage[]
      run-state.json                      # SDK RunState snapshot

chatId is the chat's ISO-8601 timestamp with : replaced by - for filesystem safety (e.g. 2025-12-14T10-00-00.000Z). Dev and staging channels (manicode-dev, manicode-staging) use the same layout and are walked automatically when present.

Token / Credit Extraction

Assistant-message usage can land in several places depending on routing. The parser tries, in order:

  1. metadata.usage — direct provider.
  2. metadata.codebuff.usage — Codebuff-tagged payload.
  3. metadata.runState.sessionState.mainAgentState.messageHistory[*].providerOptions — where RunState-stashed OpenRouter calls record totals under providerOptions.usage or providerOptions.codebuff.usage.

Both camelCase and snake_case shapes are accepted (inputTokens/input_tokens/promptTokens, etc.). Credits are read from message.credits.

Environment

  • CODEBUFF_DATA_DIR — override the base directory. Point it at a single channel root such as ~/.config/manicode-dev.

Changes

core

  • sessions/codebuff.rs — new parser with 7 unit tests
  • clients.rs — register ClientId::Codebuff (id=18)
  • scanner.rschat-messages.json pattern + multi-channel scan block
  • lib.rs — dispatch in both cached-parse and parse_local_clients loops

cli / tui

  • --codebuff flag on ClientFlags
  • Wrapped / display-name / logo URL / default_clients integration
  • TUI hotkey 'b', brand color #7C3AED, per-client tests

tests

  • tests/codebuff.rs — 3 integration tests (3 usage paths, empty/non-array)

docs

  • New README row + --codebuff example + prose list update

Verification

cargo test --workspace   # 968 tests, 0 failures
cargo clippy --workspace --all-targets -- -D warnings   # clean

Notes

  • capitalize_client("codebuff")"Codebuff"
  • Logo referenced via the CodebuffAI GitHub avatar; happy to drop in a local asset under .github/assets/client-codebuff.png on request.
  • Companion npm package @ccusage/codebuff (currently under review in ryoppippi/ccusage#954) uses the same extraction approach and can serve as a cross-reference for the data shape.

Summary by cubic

Adds Codebuff (formerly Manicode) as a first-class local client with channel-aware scanning and a resilient parser that extracts token usage from multiple paths. Integrates into CLI/TUI and fixes env handling, timestamps, provider inference, and dedup to avoid double-counting.

  • New Features

    • Added ClientId::Codebuff scanning ~/.config/manicode{,-dev,-staging} or CODEBUFF_DATA_DIR; targets projects/<project>/chats/<chatId>/chat-messages.json.
    • Parser reads usage from metadata.usage, metadata.codebuff.usage, or metadata.runState...messageHistory[*].providerOptions (camel/snake); reads credits; session id is channel/project/chatId; provider inferred from model. Wired into cached and local parsing.
    • CLI/TUI: hidden --codebuff flag, default inclusion, display name/logo, hotkey b, brand color #7C3AED. README and tests updated.
  • Bug Fixes

    • CODEBUFF_DATA_DIR trims/ignores empty values and falls back to default channels; scanner walks all three channels by default.
    • ChatId-to-timestamp restores only time separators to fix missing per-message timestamps; unknown models map to unknown provider; added stable dedup keys to prevent double-counting.
    • Run-state history continues when providerOptions is missing; logo URL switched to the Codebuff GitHub avatar.

Written for commit ea19bf7. Summary will update on new commits.


Open in Devin Review

@vercel

vercel Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
tokscale Ignored Ignored Preview Apr 26, 2026 4:18am

Request Review

cubic-dev-ai[bot]

This comment was marked as resolved.

@junhoyeo
junhoyeo force-pushed the feat/codebuff-sessions branch from f97a148 to fa40cbe Compare April 26, 2026 03:25
devin-ai-integration[bot]

This comment was marked as resolved.

@junhoyeo junhoyeo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, @anandghegde! Codebuff support is a great addition. Approving — will squash merge after #464 lands.

anandghegde and others added 4 commits April 26, 2026 13:17
Adds Codebuff (formerly Manicode) as a new first-class tokscale client.

Codebuff persists chat history under
  $CODEBUFF_DATA_DIR/projects/<project>/chats/<chatId>/chat-messages.json
with the default root at ~/.config/manicode. Dev and staging channels
(manicode-dev, manicode-staging) use the same layout and are walked
automatically when present.

core:
- sessions/codebuff.rs: new parser. Emits a UnifiedMessage per assistant
  message that carries token usage. Usage is extracted (in order) from
  metadata.usage, metadata.codebuff.usage, and the stashed
  metadata.runState.sessionState.mainAgentState.messageHistory[] (where
  OpenRouter-routed calls land their token counts). Accepts both camelCase
  and snake_case token keys so that different upstream providers (Anthropic
  SDK, OpenAI/OpenRouter, Codebuff's own shape) parse cleanly. Session id
  encodes channel + project + chatId so per-channel breakdowns remain
  distinct after aggregation. Credits are read from message.credits.
- clients.rs: register ClientId::Codebuff (id=18) with
  PathRoot::EnvVar { var: "CODEBUFF_DATA_DIR", fallback_relative:
  ".config/manicode" }; pattern "chat-messages.json".
- scanner.rs: recognise the new exact-match file pattern and add a
  custom scan block that either honours CODEBUFF_DATA_DIR or walks the
  three known channel roots.
- lib.rs: wire parse_codebuff_file into both the cached-parse and
  parse_local_clients dispatch loops.

cli / tui:
- main.rs: --codebuff flag on the global ClientFlags plus updated
  build_client_filter, capitalize_client and existing test fixtures.
- commands/wrapped.rs: Codebuff entries for client_display_name,
  client_logo_url and default_clients (the Wrapped aggregation picks
  Codebuff up automatically once registered).
- tui/client_ui.rs, tui/data/mod.rs, tui/mod.rs, tui/ui/widgets.rs:
  display name, hotkey 'b', brand color #7C3AED, and test_data_loading
  coverage for the new client.

tests:
- tests/codebuff.rs: end-to-end parse covering the three usage
  extraction paths, snake/camel schemas, and the empty/non-array
  inputs path.
- 7 new unit tests inside sessions/codebuff.rs plus 1 new client_id
  assertion and 1 env-var assertion in clients.rs.

docs:
- README.md: new row in the supported clients table plus the
  --codebuff example block and prose list update.

Verified locally: cargo test --workspace (968 tests, 0 failures)
and cargo clippy --workspace --all-targets -- -D warnings clean.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Three follow-up fixes on the initial Codebuff integration.

1. wrapped.rs — client_logo_url("Codebuff") used to point at
   https://tokscale.ai/assets/logos/codebuff.png, which has not been
   uploaded to the tokscale CDN yet and returns 404. Replace with the
   (verified) CodebuffAI GitHub organisation avatar. Matches the logo
   already used in the README table row.

2. scanner.rs — a CODEBUFF_DATA_DIR that was set to an empty or
   whitespace-only string used to be captured by the outer
   `if let Some(root) = env_override` branch, pass the inner
   `.trim().is_empty()` guard as a no-op, and then fall out of the
   block without queueing any scan tasks. Callers exporting an empty
   override (common in shells that inherit CODEBUFF_DATA_DIR= from a
   parent process) would see Codebuff silently disabled. Rework the
   override detection to trim+filter first, so whitespace values now
   fall through to the three default channel roots.

3. sessions/codebuff.rs — the previous chat-id→timestamp bridge did
   `chat_id.replace('-', ":")`, but the chatId encodes the chat's
   ISO-8601 timestamp as `2025-12-14T10-00-00.000Z` where the `-`
   separators appear in BOTH the date (YYYY-MM-DD) and the time
   (HH-MM-SS) portions. Global replacement corrupted the date to
   `2025:12:14T...`, RFC3339 parsing failed, and chat_id_ts silently
   fell back to 0 — which meant every assistant message missing its own
   timestamp/createdAt/metadata.timestamp was reported at the file
   mtime rather than the chat time. Introduce parse_chat_id_to_millis
   which only flips the first two `-` *after* the `T`.

Regression coverage:
- sessions/codebuff.rs: parse_chat_id_to_millis happy path + broken
  global-replace comparison + garbage input.
- tests/codebuff.rs: end-to-end chat-id fallback (no per-message
  timestamp) asserts the parsed UnifiedMessage.timestamp is the
  chat-id-derived epoch, not the file mtime.
- scanner.rs: three new #[serial] tests for the Codebuff env logic —
  default three-channel walk, whitespace env falls back to defaults,
  and non-empty env selects only the override root.

Verified: cargo test --workspace (972 passed, 0 failed) and
cargo clippy --workspace --all-targets -- -D warnings clean.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
… dedup keys

The previous fallback hardcoded 'anthropic' when a token-bearing message had no
recognizable model hint, skewing per-provider stats since Codebuff routes calls
through multiple providers. Falls back to 'unknown' instead.

Also adds a stable dedup_key derived from the upstream ChatMessage.id (or a
deterministic fallback combining session, timestamp, model, and token shape) so
the same chat history scanned from multiple roots no longer double-counts.
…and update client count test

The reverse history walk in extract_usage_from_run_state used the ? operator
on entry.get("providerOptions"), which exited the entire function on the
first assistant entry without providerOptions. That silently dropped valid
usage data carried by earlier entries. Replaced with let-else so the loop
continues to the next candidate.

Also updates test_client_all in tui/data to reflect that ClientId::ALL now
contains 20 entries (Goose at index 18, Codebuff at 19) after both clients
landed on main.
@junhoyeo
junhoyeo force-pushed the feat/codebuff-sessions branch from 2667412 to ea19bf7 Compare April 26, 2026 04:18
@junhoyeo
junhoyeo merged commit 32118ba into junhoyeo:main Apr 26, 2026
4 of 5 checks passed
junhoyeo added a commit that referenced this pull request Apr 26, 2026
…nd client filter UX (#467)

Pre-release correctness and security fixes across the four PRs landed this cycle (#464, #454, #359, #355).

## Antigravity

Trust boundary around the local language-server RPC was too loose:

- Bound RPC body sizes at 16 MiB (Content-Length, chunked, and read-to-end paths)
- Verify process identity by checking the executable path (`lsof` on macOS, `/proc/<pid>/exe` on Linux); accept paths containing either `antigravity` or `language_server` since some Antigravity-flavored servers launch from generic `language_server` binaries with `--app_data_dir antigravity`
- Probe candidate endpoints with a real RPC call and JSON-shape check (probe body capped at 4 KiB) instead of trusting any 200 response; consume HTTP headers before reading body so the cap applies to the JSON body alone
- Lock concurrent syncs on a per-cache PID lock file with bounded retry (3 attempts) instead of unbounded recursion. Eviction is gated on PID liveness only — long-running syncs no longer get stomped on by age-based timeouts.
- Enforce manifest version on load: future versions abort, older versions start fresh
- Recover corrupted manifests by moving them aside as `manifest.json.corrupt-<ts>` instead of failing every subsequent sync

## Codebuff

Parser correctness around silent data loss:

- Accumulate run-state usage across the full reverse `messageHistory` walk instead of returning on the first signal-bearing entry. Previously a newest assistant entry carrying only a model id would short-circuit the walk and silently drop real token counts on earlier entries.
- Include the source-array ordinal in the fallback dedup key so two id-less assistant messages with identical session/timestamp/model/tokens no longer collapse into a single record
- Reject non-positive numeric timestamps in the shared `parse_timestamp_value`/`parse_timestamp_str` helpers so messages with `timestamp: 0` or negative epochs fall through to chat-id / file-mtime fallback chain

## TUI + parsing

User-visible client-filter UX:

- Move `SYNTHETIC_HOTKEY` from `'x'` to `'n'`. `'x'` collided with Mux's hotkey, and the dispatch order made the displayed `[x]` for Synthetic purely cosmetic
- Enable `ignore_case` on `--client/-c` so `OPENCODE`, `Codebuff`, and `antigravity` all parse as the same canonical filter

## Test results

- `cargo test -p tokscale-cli` — 415 unit + 83 integration, all pass
- `cargo test -p tokscale-core` — 566 unit + 10 codebuff + 3 hermes, all pass
- `cargo check --workspace` — clean

Each fix has a regression test where applicable.
junhoyeo added a commit that referenced this pull request Apr 26, 2026
The Korean, Japanese, and Simplified Chinese READMEs were not updated when
Codebuff (#454), Goose (#457), and Antigravity (#355) landed. The English
README also picked up missing Goose and Antigravity entries during this
sweep. Sync all four to the same client list, settings table, Windows
locations, Data Sources sections, and `tokscale antigravity` command
documentation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants