Skip to content

Commit b30f8cc

Browse files
rajbosCopilot
andauthored
feat: add dedicated CLI interaction mode in Usage Analysis (#659)
Split CLI-based tool sessions (Copilot CLI, Claude Code, OpenCode, Crush, Mistral Vibe) out of Ask Mode / Agent Mode into a distinct 'CLI' interaction mode in the Usage Analysis panel. Changes: - Add 'cli' field to ModeUsage interface and ChatTurn.mode union type - Update CLI adapters (claudeCode, openCode, crush, mistralVibe) to count interactions as 'cli' instead of 'ask'/'agent' - Add teal CLI bar row in Usage Analysis webview (Today + Last 30 Days) - Add CLI mode icon/color in Log Viewer webview - Include CLI in maturity scoring (totalInteractions, agent-like evidence) - Add cliModeCount to backend rollups, sync service, and storage types - Update calculateFluencyScoreForTeamMember to include cliModeCount - Add CLI display in CLI fluency command output - Bump CACHE_VERSION (extension 40->41, cli 1->2) to invalidate stale caches - Update all test fixtures with cli: 0 field Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent bc01e3c commit b30f8cc

21 files changed

Lines changed: 78 additions & 60 deletions

cli/src/cliCache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as os from 'os';
1010
import type { SessionData } from './helpers';
1111

1212
/** Bump this when the SessionData shape changes to force a full re-parse. */
13-
const CACHE_VERSION = 1;
13+
const CACHE_VERSION = 2;
1414

1515
/** Maximum number of entries to keep in the cache file. */
1616
const MAX_CACHE_ENTRIES = 2000;

cli/src/commands/fluency.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export const fluencyCommand = new Command('fluency')
177177
console.log(chalk.dim('─'.repeat(55)));
178178
console.log(` Sessions analyzed: ${chalk.bold(fmt(p.sessions))}`);
179179

180-
const totalInteractions = p.modeUsage.ask + p.modeUsage.edit + p.modeUsage.agent;
180+
const totalInteractions = p.modeUsage.ask + p.modeUsage.edit + p.modeUsage.agent + p.modeUsage.cli;
181181
console.log(` Total interactions: ${chalk.bold(fmt(totalInteractions))}`);
182182

183183
if (p.modeUsage.ask > 0) {
@@ -189,6 +189,9 @@ export const fluencyCommand = new Command('fluency')
189189
if (p.modeUsage.agent > 0) {
190190
console.log(` Agent mode: ${fmt(p.modeUsage.agent)}`);
191191
}
192+
if (p.modeUsage.cli > 0) {
193+
console.log(` CLI: ${fmt(p.modeUsage.cli)}`);
194+
}
192195
if (p.toolCalls.total > 0) {
193196
console.log(` Tool calls: ${fmt(p.toolCalls.total)}`);
194197
}

cli/src/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ function createEmptyUsageAnalysisPeriod(): UsageAnalysisPeriod {
473473
return {
474474
sessions: 0,
475475
toolCalls: { total: 0, byTool: {} },
476-
modeUsage: { ask: 0, edit: 0, agent: 0, plan: 0, customAgent: 0 },
476+
modeUsage: { ask: 0, edit: 0, agent: 0, plan: 0, customAgent: 0, cli: 0 },
477477
contextReferences: createEmptyContextRefs(),
478478
mcpTools: { total: 0, byServer: {}, byTool: {} },
479479
modelSwitching: {

vscode-extension/src/adapters/claudeCodeAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class ClaudeCodeAdapter implements IEcosystemAdapter, IDiscoverableEcosys
7474
const models: string[] = [];
7575
for (const event of events) {
7676
if (event.type === 'user' && event.message?.role === 'user' && !event.isSidechain) {
77-
analysis.modeUsage.ask++;
77+
analysis.modeUsage.cli++;
7878
} else if (event.type === 'assistant') {
7979
const model = normalizeClaudeModelId(event.message?.model || 'unknown');
8080
models.push(model);

vscode-extension/src/adapters/crushAdapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export class CrushAdapter implements IEcosystemAdapter, IDiscoverableEcosystem,
154154
turns.push({
155155
turnNumber,
156156
timestamp: msg.created_at ? new Date(msg.created_at * 1000).toISOString() : null,
157-
mode: 'agent',
157+
mode: 'cli',
158158
userMessage: userText,
159159
assistantResponse: assistantText,
160160
model,
@@ -178,7 +178,7 @@ export class CrushAdapter implements IEcosystemAdapter, IDiscoverableEcosystem,
178178
const messages = await this.crush.getCrushMessages(sessionFile);
179179
const models: string[] = [];
180180
for (const msg of messages) {
181-
if (msg.role === 'user') { analysis.modeUsage.agent++; }
181+
if (msg.role === 'user') { analysis.modeUsage.cli++; }
182182
if (msg.role === 'assistant') {
183183
const model = msg.model || 'unknown';
184184
models.push(model);

vscode-extension/src/adapters/mistralVibeAdapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export class MistralVibeAdapter implements IEcosystemAdapter, IDiscoverableEcosy
111111
turns.push({
112112
turnNumber: t + 1,
113113
timestamp: sessionMeta.firstInteraction,
114-
mode: 'agent',
114+
mode: 'cli',
115115
userMessage: userText,
116116
assistantResponse: assistantText,
117117
model,
@@ -135,7 +135,7 @@ export class MistralVibeAdapter implements IEcosystemAdapter, IDiscoverableEcosy
135135
const models: string[] = [];
136136
for (const msg of messages) {
137137
if (msg.role === 'user' && msg.injected !== true) {
138-
analysis.modeUsage.agent++;
138+
analysis.modeUsage.cli++;
139139
} else if (msg.role === 'assistant') {
140140
models.push(model);
141141
if (Array.isArray(msg.tool_calls)) {

vscode-extension/src/adapters/openCodeAdapter.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export class OpenCodeAdapter implements IEcosystemAdapter, IDiscoverableEcosyste
195195
turns.push({
196196
turnNumber,
197197
timestamp: msg.time?.created ? new Date(msg.time.created).toISOString() : null,
198-
mode: (msg.agent === 'build' || msg.agent === 'agent') ? 'agent' : (msg.agent === 'ask' ? 'ask' : 'agent'),
198+
mode: 'cli',
199199
userMessage: userText,
200200
assistantResponse: assistantText,
201201
model,
@@ -224,11 +224,7 @@ export class OpenCodeAdapter implements IEcosystemAdapter, IDiscoverableEcosyste
224224
const models: string[] = [];
225225
for (const msg of messages) {
226226
if (msg.role === 'user') {
227-
const mode = msg.agent || 'agent';
228-
if (mode === 'build' || mode === 'agent') { analysis.modeUsage.agent++; }
229-
else if (mode === 'ask') { analysis.modeUsage.ask++; }
230-
else if (mode === 'edit') { analysis.modeUsage.edit++; }
231-
else { analysis.modeUsage.agent++; }
227+
analysis.modeUsage.cli++;
232228
}
233229
if (msg.role === 'assistant') {
234230
const model = msg.modelID || 'unknown';

vscode-extension/src/backend/rollups.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface DailyRollupValueLike {
3939
agentModeCount?: number;
4040
planModeCount?: number;
4141
customAgentModeCount?: number;
42+
cliModeCount?: number;
4243
toolCallsJson?: string;
4344
contextRefsJson?: string;
4445
mcpToolsJson?: string;
@@ -128,6 +129,9 @@ export function upsertDailyRollup(
128129
if (val.customAgentModeCount !== undefined) {
129130
ex.customAgentModeCount = (ex.customAgentModeCount || 0) + val.customAgentModeCount;
130131
}
132+
if (val.cliModeCount !== undefined) {
133+
ex.cliModeCount = (ex.cliModeCount || 0) + val.cliModeCount;
134+
}
131135
if (val.multiTurnSessions !== undefined) {
132136
ex.multiTurnSessions = (ex.multiTurnSessions || 0) + val.multiTurnSessions;
133137
}

vscode-extension/src/backend/services/syncService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ export class SyncService {
591591
fluencyMetrics.agentModeCount = Math.round((analysis.modeUsage.agent || 0) * ratio);
592592
fluencyMetrics.planModeCount = Math.round((analysis.modeUsage.plan || 0) * ratio);
593593
fluencyMetrics.customAgentModeCount = Math.round((analysis.modeUsage.customAgent || 0) * ratio);
594+
fluencyMetrics.cliModeCount = Math.round((analysis.modeUsage.cli || 0) * ratio);
594595
}
595596

596597
// Serialize complex objects as JSON

vscode-extension/src/backend/storageTables.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface BackendAggDailyEntityLike {
4747
agentModeCount?: number;
4848
planModeCount?: number;
4949
customAgentModeCount?: number;
50+
cliModeCount?: number;
5051
toolCallsJson?: string; // Serialized ToolCallUsage: { total, byTool: {...} }
5152
contextRefsJson?: string; // Serialized ContextReferenceUsage
5253
mcpToolsJson?: string; // Serialized McpToolUsage: { total, byServer, byTool }

0 commit comments

Comments
 (0)