Skip to content
Merged
6 changes: 6 additions & 0 deletions docs/TRACKABLE-DATA.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ References detected via regex:
- `#selection` - Selected code/text references
- `#symbol` - Code symbol references (functions, classes, variables)
- `#codebase` - Entire codebase references
- `#terminalLastCommand` - Last command run in terminal
- `#terminalSelection` - Selected terminal output
- `#clipboard` - Clipboard contents
- `#changes` - Uncommitted git changes
- `#outputPanel` - Output panel contents
- `#problemsPanel` - Problems panel contents
- `@workspace` - Workspace-wide context
- `@terminal` - Terminal/command-line context
- `@vscode` - VS Code settings/environment
Expand Down
11 changes: 10 additions & 1 deletion docs/USAGE-ANALYSIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ Tracks how often you provide different types of context to Copilot:
- **✂️ #selection**: References to selected code or text
- **🔤 #symbol**: References to code symbols (functions, classes, variables)
- **🗂️ #codebase**: References to the entire codebase for search/analysis
- **⌨️ #terminalLastCommand**: References to the last command run in terminal
- **🖱️ #terminalSelection**: References to selected terminal output
- **📋 #clipboard**: References to clipboard contents
- **📝 #changes**: References to uncommitted git changes
- **📤 #outputPanel**: References to output panel contents
- **⚠️ #problemsPanel**: References to problems panel contents
- **📁 @workspace**: References to workspace-wide context
- **💻 @terminal**: References to terminal or command-line context
- **🔧 @vscode**: References to VS Code settings or environment
Expand Down Expand Up @@ -125,6 +131,9 @@ Session analysis data is cached alongside token counts to improve performance:

- **High #file usage**: You often work with specific files
- **High #selection usage**: You frequently reference selected code
- **High #terminalLastCommand usage**: You often ask about terminal commands or errors
- **High #changes usage**: You frequently review uncommitted changes with Copilot
- **High #outputPanel or #problemsPanel usage**: You use Copilot to debug build/test output
- **High @workspace usage**: You provide broad context for better suggestions
- **Low context usage**: Consider providing more context for better results

Expand All @@ -141,7 +150,7 @@ Session analysis data is cached alongside token counts to improve performance:

## Tips for Optimization

1. **Provide Rich Context**: Use #file, #selection, and @workspace to give Copilot better context
1. **Provide Rich Context**: Use #file, #selection, #terminalLastCommand, #changes, and @workspace to give Copilot better context
2. **Try Different Modes**: Experiment with ask vs. edit mode for different tasks
3. **Leverage Agent Mode**: For complex tasks, consider using agent mode or Copilot CLI
4. **Monitor Tool Usage**: Tools can extend Copilot's capabilities - check which are being used
Expand Down
7 changes: 0 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

176 changes: 174 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as path from 'path';
import * as os from 'os';
import tokenEstimatorsData from './tokenEstimators.json';
import modelPricingData from './modelPricing.json';
import toolNamesData from './toolNames.json';
import { BackendFacade } from './backend/facade';
import { BackendCommandHandler } from './backend/commands';
import * as packageJson from '../package.json';
Expand Down Expand Up @@ -124,6 +125,12 @@ interface ContextReferenceUsage {
workspace: number; // @workspace references
terminal: number; // @terminal references
vscode: number; // @vscode references
terminalLastCommand: number; // #terminalLastCommand references
terminalSelection: number; // #terminalSelection references
clipboard: number; // #clipboard references
changes: number; // #changes references
outputPanel: number; // #outputPanel references
problemsPanel: number; // #problemsPanel references
// contentReferences tracking from session logs
byKind: { [kind: string]: number }; // Count by reference kind
copilotInstructions: number; // .github/copilot-instructions.md
Expand Down Expand Up @@ -259,6 +266,9 @@ class CopilotTokenTracker implements vscode.Disposable {
// These are reference prices for cost estimation purposes only
private modelPricing: { [key: string]: ModelPricing } = modelPricingData.pricing as { [key: string]: ModelPricing };

// Tool name mapping - loaded from toolNames.json for friendly display names
private toolNameMap: { [key: string]: string } = toolNamesData as { [key: string]: string };

// Helper method to get repository URL from package.json
private getRepositoryUrl(): string {
const repoUrl = packageJson.repository?.url?.replace(/^git\+/, '').replace(/\.git$/, '');
Expand Down Expand Up @@ -1124,6 +1134,12 @@ class CopilotTokenTracker implements vscode.Disposable {
workspace: 0,
terminal: 0,
vscode: 0,
terminalLastCommand: 0,
terminalSelection: 0,
clipboard: 0,
changes: 0,
outputPanel: 0,
problemsPanel: 0,
byKind: {},
copilotInstructions: 0,
agentsMd: 0,
Expand Down Expand Up @@ -1230,6 +1246,12 @@ class CopilotTokenTracker implements vscode.Disposable {
period.contextReferences.workspace += analysis.contextReferences.workspace;
period.contextReferences.terminal += analysis.contextReferences.terminal;
period.contextReferences.vscode += analysis.contextReferences.vscode;
period.contextReferences.terminalLastCommand += analysis.contextReferences.terminalLastCommand || 0;
period.contextReferences.terminalSelection += analysis.contextReferences.terminalSelection || 0;
period.contextReferences.clipboard += analysis.contextReferences.clipboard || 0;
period.contextReferences.changes += analysis.contextReferences.changes || 0;
period.contextReferences.outputPanel += analysis.contextReferences.outputPanel || 0;
period.contextReferences.problemsPanel += analysis.contextReferences.problemsPanel || 0;

// Merge contentReferences counts
period.contextReferences.copilotInstructions += analysis.contextReferences.copilotInstructions || 0;
Expand Down Expand Up @@ -1525,6 +1547,12 @@ class CopilotTokenTracker implements vscode.Disposable {
workspace: 0,
terminal: 0,
vscode: 0,
terminalLastCommand: 0,
terminalSelection: 0,
clipboard: 0,
changes: 0,
outputPanel: 0,
problemsPanel: 0,
byKind: {},
copilotInstructions: 0,
agentsMd: 0,
Expand Down Expand Up @@ -1640,11 +1668,101 @@ class CopilotTokenTracker implements vscode.Disposable {
}

// Non-delta JSONL (Copilot CLI format) - process line-by-line
let sessionMode = 'ask';
for (const line of lines) {
if (!line.trim()) { continue; }
try {
const event = JSON.parse(line);

// Handle VS Code incremental format - detect mode from session header
if (event.kind === 0 && event.v?.inputState?.mode?.kind) {
sessionMode = event.v.inputState.mode.kind;

// Detect implicit selections in initial state (only if there's an actual range)
if (event.v?.inputState?.selections && Array.isArray(event.v.inputState.selections)) {
for (const sel of event.v.inputState.selections) {
// Only count if it's an actual selection (not just a cursor position)
if (sel.startLineNumber !== sel.endLineNumber || sel.startColumn !== sel.endColumn) {
analysis.contextReferences.implicitSelection++;
break; // Count once per session
}
}
}
}

// Handle mode changes (kind: 1 with mode update)
if (event.kind === 1 && event.k?.includes('mode') && event.v?.kind) {
sessionMode = event.v.kind;
}

// Detect implicit selections in updates to inputState.selections
if (event.kind === 1 && event.k?.includes('selections') && Array.isArray(event.v)) {
for (const sel of event.v) {
// Only count if it's an actual selection (not just a cursor position)
if (sel && (sel.startLineNumber !== sel.endLineNumber || sel.startColumn !== sel.endColumn)) {
analysis.contextReferences.implicitSelection++;
break; // Count once per update
}
}
}

// Handle contentReferences updates (kind: 1 with contentReferences update)
if (event.kind === 1 && event.k?.includes('contentReferences') && Array.isArray(event.v)) {
this.analyzeContentReferences(event.v, analysis.contextReferences);
}

// Handle variableData updates (kind: 1 with variableData update)
if (event.kind === 1 && event.k?.includes('variableData') && event.v) {
this.analyzeVariableData(event.v, analysis.contextReferences);
}

// Handle VS Code incremental format - count requests as interactions
if (event.kind === 2 && event.k?.[0] === 'requests' && Array.isArray(event.v)) {
for (const request of event.v) {
if (request.requestId) {
// Count by mode
if (sessionMode === 'agent') {
analysis.modeUsage.agent++;
} else if (sessionMode === 'edit') {
analysis.modeUsage.edit++;
} else {
analysis.modeUsage.ask++;
}
}
// Check for agent in request
if (request.agent?.id) {
const toolName = request.agent.id;
analysis.toolCalls.total++;
analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1;
}

// Analyze all context references from this request
this.analyzeRequestContext(request, analysis.contextReferences);

// Extract tool calls from request.response array (when full request is added)
if (request.response && Array.isArray(request.response)) {
for (const responseItem of request.response) {
if (responseItem.kind === 'toolInvocationSerialized' || responseItem.kind === 'prepareToolInvocation') {
analysis.toolCalls.total++;
const toolName = responseItem.toolId || responseItem.toolName || responseItem.invocationMessage?.toolName || responseItem.toolSpecificData?.kind || 'unknown';
analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1;
}
}
}
}
}

// Handle VS Code incremental format - tool invocations in responses
if (event.kind === 2 && event.k?.includes('response') && Array.isArray(event.v)) {
for (const responseItem of event.v) {
if (responseItem.kind === 'toolInvocationSerialized') {
analysis.toolCalls.total++;
const toolName = responseItem.toolId || responseItem.toolName || responseItem.invocationMessage?.toolName || responseItem.toolSpecificData?.kind || 'unknown';
analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1;
}
}
}

// Handle Copilot CLI format
// Detect mode from event type - CLI can be chat or agent mode
if (event.type === 'user.message') {
Expand Down Expand Up @@ -1868,10 +1986,19 @@ class CopilotTokenTracker implements vscode.Disposable {
/**
* Extract server name from an MCP tool name.
* MCP tool names follow the format: mcp.server.tool or mcp_server_tool
* For example: "mcp.io.github.git" → "io", "mcp_io_github_git" → "io"
* Note: Splits on both '.' and '_' to handle various naming conventions
* For example: "mcp.io.github.git.assign_copilot_to_issue" → "GitHub MCP"
* Uses the display name from toolNames.json (the part before the colon).
* Falls back to extracting the second segment if no mapping exists.
*/
private extractMcpServerName(toolName: string): string {
// First, try to get the display name from toolNames.json and extract the server part
const displayName = this.toolNameMap[toolName];
if (displayName && displayName.includes(':')) {
// Extract the part before the colon (e.g., "GitHub MCP" from "GitHub MCP: Issue Read")
return displayName.split(':')[0].trim();
}

// Fallback: extract from tool name structure
// Remove the mcp. or mcp_ prefix
const withoutPrefix = toolName.replace(/^mcp[._]/, '');
// Split on . or _ and take the first part (server identifier)
Expand Down Expand Up @@ -1943,6 +2070,42 @@ class CopilotTokenTracker implements vscode.Disposable {
refs.codebase += codebaseMatches.length;
}

// Count #terminalLastCommand references
const terminalLastCommandMatches = text.match(/#terminalLastCommand/gi);
if (terminalLastCommandMatches) {
refs.terminalLastCommand += terminalLastCommandMatches.length;
}

// Count #terminalSelection references
const terminalSelectionMatches = text.match(/#terminalSelection/gi);
if (terminalSelectionMatches) {
refs.terminalSelection += terminalSelectionMatches.length;
}

// Count #clipboard references
const clipboardMatches = text.match(/#clipboard/gi);
if (clipboardMatches) {
refs.clipboard += clipboardMatches.length;
}

// Count #changes references
const changesMatches = text.match(/#changes/gi);
if (changesMatches) {
refs.changes += changesMatches.length;
}

// Count #outputPanel references
const outputPanelMatches = text.match(/#outputPanel/gi);
if (outputPanelMatches) {
refs.outputPanel += outputPanelMatches.length;
}

// Count #problemsPanel references
const problemsPanelMatches = text.match(/#problemsPanel/gi);
if (problemsPanelMatches) {
refs.problemsPanel += problemsPanelMatches.length;
}

// Count @workspace references
const workspaceMatches = text.match(/@workspace/gi);
if (workspaceMatches) {
Expand Down Expand Up @@ -2370,6 +2533,12 @@ class CopilotTokenTracker implements vscode.Disposable {
workspace: 0,
terminal: 0,
vscode: 0,
terminalLastCommand: 0,
terminalSelection: 0,
clipboard: 0,
changes: 0,
outputPanel: 0,
problemsPanel: 0,
byKind: {},
copilotInstructions: 0,
agentsMd: 0,
Expand Down Expand Up @@ -2494,6 +2663,7 @@ class CopilotTokenTracker implements vscode.Disposable {
contextReferences: {
file: 0, selection: 0, implicitSelection: 0, symbol: 0, codebase: 0,
workspace: 0, terminal: 0, vscode: 0,
terminalLastCommand: 0, terminalSelection: 0, clipboard: 0, changes: 0, outputPanel: 0, problemsPanel: 0,
// Extended fields expected by SessionUsageAnalysis in the webview
byKind: {}, copilotInstructions: 0, agentsMd: 0, byPath: {}
},
Expand Down Expand Up @@ -2550,6 +2720,7 @@ class CopilotTokenTracker implements vscode.Disposable {
contextReferences: {
file: 0, selection: 0, implicitSelection: 0, symbol: 0, codebase: 0,
workspace: 0, terminal: 0, vscode: 0,
terminalLastCommand: 0, terminalSelection: 0, clipboard: 0, changes: 0, outputPanel: 0, problemsPanel: 0,
byKind: {}, copilotInstructions: 0, agentsMd: 0, byPath: {}
},
firstInteraction: null,
Expand Down Expand Up @@ -3058,6 +3229,7 @@ class CopilotTokenTracker implements vscode.Disposable {
return {
file: 0, selection: 0, implicitSelection: 0, symbol: 0, codebase: 0,
workspace: 0, terminal: 0, vscode: 0,
terminalLastCommand: 0, terminalSelection: 0, clipboard: 0, changes: 0, outputPanel: 0, problemsPanel: 0,
byKind: {}, copilotInstructions: 0, agentsMd: 0, byPath: {}
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/toolNames.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
,"mcp_io_github_git_pull_request_read": "GitHub MCP: Pull Request Read"
,"mcp_io_github_git_issue_read": "GitHub MCP: Issue Read"
,"mcp_io_github_git_issue_write": "GitHub MCP: Issue Write"
,"mcp_io_github_git_get_file_contents": "GitHub MCP: Get File Contents"
,"mcp_io_github_git_search_code": "GitHub MCP: Search Code"
,"mcp_io_github_git_search_pull_requests": "GitHub MCP: Search Pull Requests"
,"mcp_com_microsoft_get_bestpractices": "GitHub MCP: Get Best Practices"
,"manage_todo_list": "Manage TODO List"
,"copilot_readFile": "Read File"
,"copilot_applyPatch": "Apply Patch"
Expand All @@ -23,4 +27,5 @@
,"github.copilot.editsAgent": "GitHub Copilot Edits Agent"
,"todoList": "TODO List"
,"terminal": "Terminal"
,"terminal_last_command": "Terminal Last Command"
}
Loading
Loading