Skip to content

Commit 710f675

Browse files
authored
Merge pull request #9070 from continuedev/nate/refactor-info-screen-extract-functions
Refactor info command into separate functions and add usage statistics
2 parents 3e7bf60 + 30b8d5c commit 710f675

File tree

2 files changed

+94
-45
lines changed

2 files changed

+94
-45
lines changed

docs/cli/quick-start.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ Common slash commands available in CLI:
141141
- `/exit` - Exit the chat
142142
- `/fork` - Start a forked chat session from the current history
143143
- `/help` - Show help message
144-
- `/info` - Show session information
144+
- `/info` - Show session information including usage statistics (cost, tokens, cache usage)
145145
- `/init` - Create an AGENTS.md file
146146
- `/login` - Authenticate with your account
147147
- `/logout` - Sign out of your current session

extensions/cli/src/infoScreen.ts

Lines changed: 93 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,97 +8,139 @@ import {
88
loadAuthConfig,
99
} from "./auth/workos.js";
1010
import { services } from "./services/index.js";
11-
import { getCurrentSession, getSessionFilePath } from "./session.js";
11+
import {
12+
getCurrentSession,
13+
getSessionFilePath,
14+
getSessionUsage,
15+
} from "./session.js";
1216
import { logger } from "./util/logger.js";
1317
import { getVersion } from "./version.js";
1418

15-
export async function handleInfoSlashCommand() {
16-
const infoLines = [];
17-
18-
// Version and working directory info
19+
function getVersionInfo(): string[] {
1920
const version = getVersion();
2021
const cwd = process.cwd();
2122

22-
infoLines.push(
23+
return [
2324
chalk.white("CLI Information:"),
2425
` Version: ${chalk.green(version)}`,
2526
` Working Directory: ${chalk.blue(cwd)}`,
26-
);
27+
];
28+
}
29+
30+
async function getAuthInfo(): Promise<string[]> {
31+
const lines: string[] = ["", chalk.white("Authentication:")];
2732

28-
// Auth info
2933
if (await isAuthenticated()) {
3034
const config = loadAuthConfig();
3135
if (config && isAuthenticatedConfig(config)) {
3236
const email = config.userEmail || config.userId;
3337
const orgId = config.organizationId;
34-
infoLines.push(
35-
"",
36-
chalk.white("Authentication:"),
38+
lines.push(
3739
` Email: ${chalk.green(email)}`,
3840
` Org ID: ${chalk.cyan(orgId)}`,
3941
);
4042
} else {
41-
infoLines.push(
42-
"",
43-
chalk.white("Authentication:"),
44-
` ${chalk.yellow("Authenticated via environment variable")}`,
45-
);
43+
lines.push(` ${chalk.yellow("Authenticated via environment variable")}`);
4644
}
4745
} else {
48-
infoLines.push(
49-
"",
50-
chalk.white("Authentication:"),
51-
` ${chalk.red("Not logged in")}`,
52-
);
46+
lines.push(` ${chalk.red("Not logged in")}`);
5347
}
5448

55-
// Config info
49+
return lines;
50+
}
51+
52+
function getConfigInfo(): string[] {
53+
const lines: string[] = ["", chalk.white("Configuration:")];
54+
5655
try {
5756
const configState = services.config.getState();
58-
infoLines.push("", chalk.white("Configuration:"));
5957
if (configState.config) {
60-
infoLines.push(` ${chalk.gray(`Using ${configState.config?.name}`)}`);
58+
lines.push(` ${chalk.gray(`Using ${configState.config?.name}`)}`);
6159
} else {
62-
infoLines.push(` ${chalk.red(`Config not found`)}`);
60+
lines.push(` ${chalk.red(`Config not found`)}`);
6361
}
6462
if (configState.configPath) {
65-
infoLines.push(` Path: ${chalk.blue(configState.configPath)}`);
63+
lines.push(` Path: ${chalk.blue(configState.configPath)}`);
6664
}
6765

6866
// Add current model info
6967
try {
7068
const modelInfo = services.model?.getModelInfo();
7169
if (modelInfo) {
72-
infoLines.push(` Model: ${chalk.cyan(modelInfo.name)}`);
70+
lines.push(` Model: ${chalk.cyan(modelInfo.name)}`);
7371
} else {
74-
infoLines.push(` Model: ${chalk.red("Not available")}`);
72+
lines.push(` Model: ${chalk.red("Not available")}`);
7573
}
7674
} catch {
77-
infoLines.push(` Model: ${chalk.red("Error retrieving model info")}`);
75+
lines.push(` Model: ${chalk.red("Error retrieving model info")}`);
7876
}
7977
} catch {
80-
infoLines.push(
81-
"",
82-
chalk.white("Configuration:"),
83-
` ${chalk.red("Configuration service not available")}`,
84-
);
78+
lines.push(` ${chalk.red("Configuration service not available")}`);
8579
}
8680

87-
// Session info
88-
infoLines.push("", chalk.white("Session:"));
81+
return lines;
82+
}
83+
84+
function getSessionInfo(): string[] {
85+
const lines: string[] = ["", chalk.white("Session:")];
86+
8987
try {
9088
const currentSession = getCurrentSession();
9189
const sessionFilePath = getSessionFilePath();
92-
infoLines.push(
90+
lines.push(
9391
` Title: ${chalk.green(currentSession.title)}`,
9492
` ID: ${chalk.gray(currentSession.sessionId)}`,
9593
` File: ${chalk.blue(sessionFilePath)}`,
9694
);
9795
} catch {
98-
infoLines.push(` ${chalk.red("Session not available")}`);
96+
lines.push(` ${chalk.red("Session not available")}`);
9997
}
10098

101-
// Runtime diagnostic info
99+
return lines;
100+
}
101+
102+
function getUsageInfo(): string[] {
103+
const lines: string[] = ["", chalk.white("Usage:")];
104+
105+
try {
106+
const usage = getSessionUsage();
107+
if (usage.totalCost > 0) {
108+
lines.push(
109+
` Total Cost: ${chalk.green(`$${usage.totalCost.toFixed(6)}`)}`,
110+
);
111+
lines.push(
112+
` Input Tokens: ${chalk.cyan(usage.promptTokens.toLocaleString())}`,
113+
);
114+
lines.push(
115+
` Output Tokens: ${chalk.cyan(usage.completionTokens.toLocaleString())}`,
116+
);
117+
118+
if (usage.promptTokensDetails?.cachedTokens) {
119+
lines.push(
120+
` Cache Read Tokens: ${chalk.cyan(usage.promptTokensDetails.cachedTokens.toLocaleString())}`,
121+
);
122+
}
123+
124+
if (usage.promptTokensDetails?.cacheWriteTokens) {
125+
lines.push(
126+
` Cache Write Tokens: ${chalk.cyan(usage.promptTokensDetails.cacheWriteTokens.toLocaleString())}`,
127+
);
128+
}
129+
130+
const totalTokens = usage.promptTokens + usage.completionTokens;
131+
lines.push(` Total Tokens: ${chalk.cyan(totalTokens.toLocaleString())}`);
132+
} else {
133+
lines.push(` ${chalk.gray("No usage data yet")}`);
134+
}
135+
} catch (error) {
136+
logger.warn("Failed to get session usage:", error);
137+
lines.push(` ${chalk.red("Usage data not available")}`);
138+
}
139+
140+
return lines;
141+
}
142+
143+
function getDiagnosticInfo(): string[] {
102144
const nodePath = process.execPath;
103145
const invokedPath = process.argv[1];
104146

@@ -111,20 +153,27 @@ export async function handleInfoSlashCommand() {
111153
stdio: ["pipe", "pipe", "ignore"],
112154
}).trim();
113155
} catch (error) {
114-
// If npm command fails, fallback to "unknown"
115156
logger.warn("Failed to get npm version:", error);
116157
}
117158

118-
// Diagnostic info
119-
infoLines.push(
159+
return [
120160
"",
121161
chalk.white("Diagnostic Info"),
122162
` Currently running: npm-global (${chalk.green(npmVersion)})`,
123163
` Path: ${chalk.blue(nodePath)}`,
124164
` Invoked: ${chalk.blue(invokedPath)}`,
125-
);
165+
];
166+
}
126167

127-
// TODO add global settings like auto update etc.
168+
export async function handleInfoSlashCommand() {
169+
const infoLines = [
170+
...getVersionInfo(),
171+
...(await getAuthInfo()),
172+
...getConfigInfo(),
173+
...getSessionInfo(),
174+
...getUsageInfo(),
175+
...getDiagnosticInfo(),
176+
];
128177

129178
return {
130179
exit: false,

0 commit comments

Comments
 (0)