Compare commits

..
1 Commits
Author SHA1 Message Date
jaredandClaude Opus 5.5 bf8821f3f9 feat: v0.9.5 context in tokens as well as percent
build-deb / Lint, test, package (push) Successful in 2m17s
build-deb / Deploy to CT 126 (push) Successful in 8s
build-deb / Notify on failure (push) Skipped
Claude's footer reads "ctx 34% 346k/1M"; the Sessions panel and the
session switcher use the same label via ctxLabel(). Closes #43

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHRLCCdXYm41Qx5kNhehaP
2026-10-11 23:07:04 -04:00
5 changed files with 26 additions and 6 deletions
+8
View File
@@ -1,3 +1,11 @@
devdesk (0.9.5) bookworm; urgency=medium
* Context use is shown in tokens as well as percent (#43): Claude's
footer shows "ctx 34% 346k/1M", and the Sessions panel and session
switcher show "ctx 34% · 346k/1M".
-- Jared Vititoe <jjvititoe1@gmail.com> Sun, 11 Oct 2026 23:07:00 -0400
devdesk (0.9.4) bookworm; urgency=medium
* The inbox badge no longer only grows (#42). Opening the bell marks its
+6 -2
View File
@@ -43,16 +43,20 @@ if [[ $session =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$ ]]; then
fi
fi
# Footer line: session · model · context % · plan use. ANSI colour numbers, not
# Footer line: session · model · context % and tokens · plan use. ANSI colour numbers, not
# RGB, so the browser's dark or light palette applies (devdesk#31). Claude sends
# rate_limits only to Pro/Max subscribers; there .cost is an API-price estimate,
# not a bill, so show the 5-hour plan use instead and keep $ for API-key billing.
jq -rM --arg s "$session" '
def c($sgr; $t): "\u001b[\($sgr)m\($t)\u001b[0m";
def pct: if . == null then "—" else "\(. | floor)%" end;
def tok: if . >= 1000000 then "\(. / 100000 | round / 10)M" elif . >= 1000 then "\(. / 1000 | round)k" else "\(.)" end;
def ctxtok: .context_window | (.current_usage | if . then (.input_tokens // 0) + (.cache_creation_input_tokens // 0)
+ (.cache_read_input_tokens // 0) else null end) as $u
| if $u == null then "" else " \($u | tok)\(if .context_window_size then "/\(.context_window_size | tok)" else "" end)" end;
[ (if $s != "" then c("33"; "◈ \($s)") else empty end),
c("36"; .model.display_name // "Claude"),
c("37"; "ctx \(.context_window.used_percentage | pct)"),
c("37"; "ctx \(.context_window.used_percentage | pct)\(ctxtok)"),
(if .rate_limits.five_hour.used_percentage != null
then c("37"; "5h \(.rate_limits.five_hour.used_percentage | pct)")
else c("37"; "$\((.cost.total_cost_usd // 0) * 100 | round / 100)") end)
+1 -1
View File
@@ -195,7 +195,7 @@ claude_args() {
"cost":{"total_cost_usd":1.5,"total_lines_added":10,"total_lines_removed":2}}'
run bash -c "echo '$status_json' | TMUX='$DEVDESK_SOCKET,1,0' TMUX_PANE='$pane' '$REPO/libexec/devdesk-statusline'"
[ "$status" -eq 0 ]
[[ $output == *"alpha"* && $output == *"ctx 34%"* ]]
[[ $output == *"alpha"* && $output == *"ctx 34% 346k/1M"* ]]
run devdesk attach --list
[ "$(jq -r '.sessions[] | select(.name=="alpha") | .context.used_tokens' <<<"$output")" = 346000 ]
[ "$(jq -r '.sessions[] | select(.name=="alpha") | .context.model' <<<"$output")" = Opus ]
+2 -2
View File
@@ -5,7 +5,7 @@ import {
TtydConnection, fetchAlerts, fetchRepo, fetchToken, investigateAlert, sessionAction, uploadFile, AuthExpiredError, UnauthorizedError,
} from './connection.js';
import { parseTitle, pasteSafe, sessionFromSearch } from './protocol.js';
import { SessionsPanel, recentNames, rememberSession, sessionStatus } from './sessions.js';
import { SessionsPanel, ctxLabel, recentNames, rememberSession, sessionStatus } from './sessions.js';
import { renderAlerts } from './alerts.js';
import { renderRepo } from './repo.js';
import { bindAskClaude } from './widgets.js';
@@ -547,7 +547,7 @@ function renderSwitcher() {
};
const items = sessions.map((s) => {
const status = sessionStatus(s);
const ctx = s.context?.used_pct != null ? ` · ctx ${Math.floor(s.context.used_pct)}%` : '';
const ctx = s.context?.used_pct != null ? ` · ${ctxLabel(s.context)}` : '';
return el('button', {
type: 'button', role: 'menuitem',
class: `dd-menu-item dd-switch-item is-${status.key}${s.name === state.session ? ' is-current' : ''}`,
+9 -1
View File
@@ -46,6 +46,14 @@ function renameRecent(from, to) {
writeRecents(readRecents().map((r) => (r.name === from ? { ...r, name: to } : r)));
}
// "ctx 34% · 346k/1M": context use as a share of the window and in tokens.
const tokens = (n) => (n >= 1e6 ? `${+(n / 1e6).toFixed(n % 1e6 ? 1 : 0)}M` : n >= 1000 ? `${Math.round(n / 1000)}k` : String(n));
export function ctxLabel(c) {
if (c?.used_pct == null) return '';
const count = c.used_tokens != null ? ` · ${tokens(c.used_tokens)}${c.window ? `/${tokens(c.window)}` : ''}` : '';
return `ctx ${Math.floor(c.used_pct)}%${count}`;
}
export function ago(epochSeconds) {
if (!epochSeconds) return '';
const s = Math.max(0, Date.now() / 1000 - epochSeconds);
@@ -267,7 +275,7 @@ export class SessionsPanel {
renderSession(s) {
const isCurrent = s.name === this.current;
const status = sessionStatus(s);
const ctx = s.context?.used_pct != null ? `ctx ${Math.floor(s.context.used_pct)}%` : '';
const ctx = ctxLabel(s.context);
const since = s.status?.since ? ago(s.status.since) : ago(s.activity);
const model = isCurrent && s.context?.model ? s.context.model.replace(/^Claude\s+/i, '') : '';
const meta = s.running