mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2025-12-11 16:09:43 +00:00
Add subscription detection and usage badge to status bar
- Detect user subscription type (Pro/Max) via CLI initialize request - Cache subscription type in globalState for persistence - Show clickable usage badge with chart icon in status bar - Plan users: show "Max Plan" badge, opens live usage view - API users: show cost badge, opens recent usage history - Badge opens terminal in editor with ccusage command 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -120,6 +120,8 @@ class ClaudeChatProvider {
|
||||
private _totalTokensInput: number = 0;
|
||||
private _totalTokensOutput: number = 0;
|
||||
private _requestCount: number = 0;
|
||||
private _subscriptionType: string | undefined; // 'pro', 'max', or undefined for API users
|
||||
private _accountInfoFetchedThisSession: boolean = false; // Track if we fetched account info this session
|
||||
private _currentSessionId: string | undefined;
|
||||
private _backupRepoPath: string | undefined;
|
||||
private _commits: Array<{ id: string, sha: string, message: string, timestamp: string }> = [];
|
||||
@@ -168,6 +170,9 @@ class ClaudeChatProvider {
|
||||
// Load saved model preference
|
||||
this._selectedModel = this._context.workspaceState.get('claude.selectedModel', 'default');
|
||||
|
||||
// Load cached subscription type (will be refreshed on first message)
|
||||
this._subscriptionType = this._context.globalState.get('claude.subscriptionType');
|
||||
|
||||
// Resume session from latest conversation
|
||||
const latestConversation = this._getLatestConversation();
|
||||
this._currentSessionId = latestConversation?.sessionId;
|
||||
@@ -255,6 +260,16 @@ class ClaudeChatProvider {
|
||||
model: this._selectedModel
|
||||
});
|
||||
|
||||
// Send cached subscription type to webview (will be refreshed on first message)
|
||||
if (this._subscriptionType) {
|
||||
this._postMessage({
|
||||
type: 'accountInfo',
|
||||
data: {
|
||||
subscriptionType: this._subscriptionType
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Send platform information to webview
|
||||
this._sendPlatformInfo();
|
||||
|
||||
@@ -311,6 +326,9 @@ class ClaudeChatProvider {
|
||||
case 'openModelTerminal':
|
||||
this._openModelTerminal();
|
||||
return;
|
||||
case 'viewUsage':
|
||||
this._openUsageTerminal(message.usageType);
|
||||
return;
|
||||
case 'executeSlashCommand':
|
||||
this._executeSlashCommand(message.command);
|
||||
return;
|
||||
@@ -597,6 +615,19 @@ class ClaudeChatProvider {
|
||||
// Send the message to Claude's stdin as JSON (stream-json input format)
|
||||
// Don't end stdin yet - we need to keep it open for permission responses
|
||||
if (claudeProcess.stdin) {
|
||||
// First, send an initialize request to get account info (once per session)
|
||||
if (!this._accountInfoFetchedThisSession) {
|
||||
this._accountInfoFetchedThisSession = true;
|
||||
const initRequest = {
|
||||
type: 'control_request',
|
||||
request_id: 'init-' + Date.now(),
|
||||
request: {
|
||||
subtype: 'initialize'
|
||||
}
|
||||
};
|
||||
claudeProcess.stdin.write(JSON.stringify(initRequest) + '\n');
|
||||
}
|
||||
|
||||
const userMessage = {
|
||||
type: 'user',
|
||||
session_id: this._currentSessionId || '',
|
||||
@@ -633,6 +664,12 @@ class ClaudeChatProvider {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle control_response messages (responses to our initialize request)
|
||||
if (jsonData.type === 'control_response') {
|
||||
this._handleControlResponse(jsonData);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle result message - end stdin when done
|
||||
if (jsonData.type === 'result') {
|
||||
if (claudeProcess.stdin && !claudeProcess.stdin.destroyed) {
|
||||
@@ -1404,6 +1441,38 @@ class ClaudeChatProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle control_response messages from Claude CLI via stdio
|
||||
* This is used to get account info from the initialize request
|
||||
*/
|
||||
private _handleControlResponse(controlResponse: any): void {
|
||||
// Structure: controlResponse.response.response.account
|
||||
// The outer response has subtype/request_id, inner response has the actual data
|
||||
const innerResponse = controlResponse.response?.response;
|
||||
|
||||
// Check if this is an initialize response with account info
|
||||
if (innerResponse?.account) {
|
||||
const account = innerResponse.account;
|
||||
this._subscriptionType = account.subscriptionType;
|
||||
|
||||
console.log('Account info received:', {
|
||||
subscriptionType: account.subscriptionType,
|
||||
email: account.email
|
||||
});
|
||||
|
||||
// Save to globalState for persistence
|
||||
this._context.globalState.update('claude.subscriptionType', this._subscriptionType);
|
||||
|
||||
// Send subscription type to UI
|
||||
this._postMessage({
|
||||
type: 'accountInfo',
|
||||
data: {
|
||||
subscriptionType: this._subscriptionType
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle control_request messages from Claude CLI via stdio
|
||||
* This is the new permission flow that replaces the MCP file-based approach
|
||||
@@ -2636,6 +2705,23 @@ class ClaudeChatProvider {
|
||||
});
|
||||
}
|
||||
|
||||
private _openUsageTerminal(usageType: string): void {
|
||||
const terminal = vscode.window.createTerminal({
|
||||
name: 'Claude Usage',
|
||||
location: vscode.TerminalLocation.Editor
|
||||
});
|
||||
|
||||
if (usageType === 'plan') {
|
||||
// Plan users get live usage view
|
||||
terminal.sendText('npx -y ccusage blocks --live');
|
||||
} else {
|
||||
// API users get recent usage history
|
||||
terminal.sendText('npx -y ccusage blocks --recent --order desc');
|
||||
}
|
||||
|
||||
terminal.show();
|
||||
}
|
||||
|
||||
private _executeSlashCommand(command: string): void {
|
||||
// Handle /compact in chat instead of spawning a terminal
|
||||
if (command === 'compact') {
|
||||
|
||||
Reference in New Issue
Block a user