Skip to content

Commit 1aa9250

Browse files
committed
First working draft of the exteion
1 parent 1131b00 commit 1aa9250

File tree

11 files changed

+484
-53
lines changed

11 files changed

+484
-53
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Debugging the Extension - Zero Token Count Issue
2+
3+
## How to View Logs
4+
5+
1. **Press F5** to start debugging (or use the experimental instance if already running)
6+
2. In the experimental instance:
7+
- Go to **View > Output** (or press `Ctrl+Alt+O`)
8+
- In the dropdown **"Show output from:"**, select **"Copilot Token Tracker"**
9+
10+
## What the Logs Will Tell You
11+
12+
When the toolbar shows "0 | 0", check these log entries:
13+
14+
### 1. Extension Initialization
15+
```
16+
[HH:mm:ss] === Copilot Token Tracker Extension Starting ===
17+
[HH:mm:ss] Package GUID: 6B8CA5B3-1A9F-4C2E-8F3D-7E2A1B4C9D0F
18+
[HH:mm:ss] Initializing commands...
19+
[HH:mm:ss] === Extension Initialized Successfully ===
20+
```
21+
**Good**: Extension loaded properly
22+
23+
### 2. Session Discovery
24+
```
25+
[HH:mm:ss] Refreshing token stats...
26+
[HH:mm:ss] Starting stats build...
27+
[HH:mm:ss] Discovering sessions from logs...
28+
[HH:mm:ss] Checking log directory: C:\Users\[username]\AppData\Local\Temp\VSGitHubCopilotLogs
29+
[HH:mm:ss] Found X log files
30+
[HH:mm:ss] Found Y sessions from logs
31+
```
32+
33+
**Possible Issues:**
34+
-**"Log directory not found"**: Visual Studio hasn't created Copilot Chat logs yet
35+
-**"Found 0 log files"**: No recent Copilot Chat activity
36+
-**"Found 0 sessions from logs"**: Log files don't contain session paths
37+
38+
### 3. Filesystem Discovery
39+
```
40+
[HH:mm:ss] Discovering sessions from filesystem...
41+
[HH:mm:ss] Scanning filesystem starting from home: C:\Users\[username]
42+
[HH:mm:ss] Added scan root: C:\repos
43+
[HH:mm:ss] Added scan root: C:\code
44+
[HH:mm:ss] Found copilot-chat dir: C:\path\to\.vs\solution\copilot-chat
45+
[HH:mm:ss] Found Z session files in: C:\path\to\.vs\solution\copilot-chat\hash\sessions
46+
[HH:mm:ss] Found N additional sessions from filesystem
47+
```
48+
49+
**Possible Issues:**
50+
-**No "Found copilot-chat dir" messages**: No Visual Studio solutions with Copilot Chat usage
51+
-**"Found 0 session files"**: The sessions directory exists but is empty
52+
53+
### 4. Stats Aggregation
54+
```
55+
[HH:mm:ss] Discovered M session files
56+
[HH:mm:ss] Successfully parsed P sessions
57+
[HH:mm:ss] Stats aggregated - Today: 1,234, Last30Days: 45,678
58+
[HH:mm:ss] Stats updated: Today=1234, Last30Days=45678
59+
```
60+
61+
**Possible Issues:**
62+
-**"Discovered 0 session files"**: No sessions found anywhere
63+
-**"Successfully parsed 0 sessions"**: Session files exist but couldn't be parsed (wrong format/corrupted)
64+
-**"Today: 0, Last30Days: 0"**: Sessions parsed but all are older than 30 days OR token data missing
65+
66+
## Common Causes of "0 | 0"
67+
68+
### 1. Never Used Visual Studio Copilot Chat
69+
The extension only tracks **Visual Studio Copilot Chat** sessions, not VS Code or GitHub.com.
70+
71+
**Solution**: Use Copilot Chat in Visual Studio at least once:
72+
- Open Copilot Chat pane: **View > Copilot Chat**
73+
- Ask a question
74+
- Wait 5 minutes for the extension to refresh stats
75+
76+
### 2. Session Files in Unexpected Location
77+
The extension looks for session files in:
78+
- Log path: `%LOCALAPPDATA%\Temp\VSGitHubCopilotLogs\*.chat.log`
79+
- Filesystem: `.vs\{solution}\copilot-chat\{hash}\sessions\`
80+
81+
**Solution**: Check the log output to see what paths it's scanning and compare with where your session files actually are.
82+
83+
### 3. Old Session Files
84+
If all your Copilot Chat usage is older than 30 days, both "Today" and "Last30Days" will be zero.
85+
86+
**Solution**: Use Copilot Chat in the current Visual Studio session.
87+
88+
### 4. Parsing Failures
89+
Session files might exist but fail to parse (format changes, corruption, etc.).
90+
91+
**Solution**: Look for ERROR messages in the logs about parsing failures.
92+
93+
## Next Steps
94+
95+
After reviewing the logs, you'll know:
96+
1. ✅ Are session files being discovered?
97+
2. ✅ Are they being parsed successfully?
98+
3. ✅ Are they within the 30-day window?
99+
4. ✅ Do they contain token usage data?
100+
101+
Share the relevant log entries and I can help debug further!

visualstudio-extension/src/CopilotTokenTracker/.vs/CopilotTokenTracker.csproj.dtbcache.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

visualstudio-extension/src/CopilotTokenTracker/Commands/ShowTokenTrackerCommand.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public static async Task InitializeAsync(AsyncPackage package)
3737

3838
private void Execute(object sender, EventArgs e)
3939
{
40+
ThreadHelper.ThrowIfNotOnUIThread();
41+
Utilities.OutputLogger.Log("ShowTokenTrackerCommand executed - opening tool window");
42+
4043
_ = _package.JoinableTaskFactory.RunAsync(async () =>
4144
{
4245
var window = await _package.ShowToolWindowAsync(
@@ -47,7 +50,13 @@ private void Execute(object sender, EventArgs e)
4750

4851
if (window is ToolWindow.TokenTrackerToolWindow trackerWindow)
4952
{
53+
Utilities.OutputLogger.Log("Tool window opened, refreshing data...");
5054
await trackerWindow.RefreshAsync();
55+
Utilities.OutputLogger.Log("Data refresh completed");
56+
}
57+
else
58+
{
59+
Utilities.OutputLogger.LogWarning("Tool window opened but type cast failed");
5160
}
5261
});
5362
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System;
2+
using System.ComponentModel.Design;
3+
using System.Globalization;
4+
using System.Threading;
5+
using CopilotTokenTracker.Data;
6+
using Microsoft.VisualStudio.Shell;
7+
using Microsoft.VisualStudio.Shell.Interop;
8+
using Task = System.Threading.Tasks.Task;
9+
10+
namespace CopilotTokenTracker.Commands
11+
{
12+
/// <summary>
13+
/// Shows token usage in both the VS status bar (bottom) and the custom toolbar.
14+
///
15+
/// The status bar text is re-applied every 10 seconds because VS frequently
16+
/// overwrites it with "Ready". Stats are fetched every 5 minutes; the cached
17+
/// text is written to the status bar on the faster cadence.
18+
/// </summary>
19+
internal sealed class ToolbarInfoCommand
20+
{
21+
public static readonly Guid CommandSet = new Guid(ShowTokenTrackerCommand.CommandSetGuidString);
22+
public const int CommandId = 0x0200;
23+
24+
private static readonly TimeSpan StatsRefreshInterval = TimeSpan.FromMinutes(5);
25+
private static readonly TimeSpan StatusBarTickInterval = TimeSpan.FromSeconds(10);
26+
27+
private readonly AsyncPackage _package;
28+
private readonly OleMenuCommand _menuCommand;
29+
private Timer? _statsTimer;
30+
private Timer? _statusBarTimer;
31+
32+
/// <summary>Cached text written to the status bar on every tick.</summary>
33+
private volatile string _statusText = "Copilot: loading\u2026";
34+
35+
private ToolbarInfoCommand(AsyncPackage package, OleMenuCommandService commandService)
36+
{
37+
_package = package ?? throw new ArgumentNullException(nameof(package));
38+
39+
var id = new CommandID(CommandSet, CommandId);
40+
_menuCommand = new OleMenuCommand(OnClick, id);
41+
commandService.AddCommand(_menuCommand);
42+
43+
// Fetch stats after a short delay, then every 5 minutes
44+
_statsTimer = new Timer(
45+
_ => _package.JoinableTaskFactory.RunAsync(async () => await RefreshStatsAsync()),
46+
null,
47+
TimeSpan.FromSeconds(3),
48+
StatsRefreshInterval);
49+
50+
// Re-apply cached text to the status bar every 10 seconds
51+
_statusBarTimer = new Timer(
52+
_ => _package.JoinableTaskFactory.RunAsync(async () => await WriteStatusBarAsync()),
53+
null,
54+
TimeSpan.FromSeconds(5),
55+
StatusBarTickInterval);
56+
}
57+
58+
public static async Task InitializeAsync(AsyncPackage package)
59+
{
60+
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
61+
Utilities.OutputLogger.Log("Initializing ToolbarInfoCommand...");
62+
var svc = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService
63+
?? throw new InvalidOperationException("IMenuCommandService unavailable.");
64+
_ = new ToolbarInfoCommand(package, svc);
65+
Utilities.OutputLogger.Log("ToolbarInfoCommand initialized - timers started");
66+
}
67+
68+
/// <summary>Fetch fresh stats and update both toolbar button and cached status text.</summary>
69+
private async Task RefreshStatsAsync()
70+
{
71+
try
72+
{
73+
var stats = await StatsBuilder.BuildAsync();
74+
var today = FormatTokenCount(stats.Today.Tokens);
75+
var last30 = FormatTokenCount(stats.Last30Days.Tokens);
76+
var text = $"Copilot: {today} | {last30}";
77+
78+
_statusText = text;
79+
80+
await _package.JoinableTaskFactory.SwitchToMainThreadAsync(_package.DisposalToken);
81+
_menuCommand.Text = text;
82+
}
83+
catch
84+
{
85+
_statusText = "Copilot: error";
86+
try
87+
{
88+
await _package.JoinableTaskFactory.SwitchToMainThreadAsync(_package.DisposalToken);
89+
_menuCommand.Text = _statusText;
90+
}
91+
catch { /* package may be disposed */ }
92+
}
93+
}
94+
95+
/// <summary>Write the cached status text to the VS status bar.</summary>
96+
private async Task WriteStatusBarAsync()
97+
{
98+
try
99+
{
100+
await _package.JoinableTaskFactory.SwitchToMainThreadAsync(_package.DisposalToken);
101+
if (await _package.GetServiceAsync(typeof(SVsStatusbar)) is IVsStatusbar statusBar)
102+
{
103+
statusBar.SetText(_statusText);
104+
}
105+
}
106+
catch { /* ignore — VS may be shutting down or package disposed */ }
107+
}
108+
109+
private static string FormatTokenCount(long tokens)
110+
{
111+
if (tokens >= 1_000_000)
112+
return (tokens / 1_000_000.0).ToString("0.#", CultureInfo.InvariantCulture) + "M";
113+
if (tokens >= 1_000)
114+
return (tokens / 1_000.0).ToString("0.#", CultureInfo.InvariantCulture) + "K";
115+
return tokens.ToString("N0", CultureInfo.InvariantCulture);
116+
}
117+
118+
private void OnClick(object sender, EventArgs e)
119+
{
120+
_ = _package.JoinableTaskFactory.RunAsync(async () =>
121+
{
122+
var window = await _package.ShowToolWindowAsync(
123+
typeof(ToolWindow.TokenTrackerToolWindow),
124+
id: 0,
125+
create: true,
126+
cancellationToken: _package.DisposalToken);
127+
128+
if (window is ToolWindow.TokenTrackerToolWindow trackerWindow)
129+
{
130+
await trackerWindow.RefreshAsync();
131+
}
132+
});
133+
}
134+
}
135+
}

visualstudio-extension/src/CopilotTokenTracker/CopilotTokenTracker.csproj

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@
4747
<ErrorReport>prompt</ErrorReport>
4848
<WarningLevel>4</WarningLevel>
4949
</PropertyGroup>
50-
5150
<!-- Source files -->
5251
<ItemGroup>
5352
<Compile Include="CopilotTokenTrackerPackage.cs" />
5453
<Compile Include="Commands\ShowTokenTrackerCommand.cs" />
54+
<Compile Include="Commands\ToolbarInfoCommand.cs" />
5555
<Compile Include="Data\Models.cs" />
5656
<Compile Include="Data\SessionDiscovery.cs" />
5757
<Compile Include="Data\SessionParser.cs" />
@@ -63,10 +63,10 @@
6363
<DependentUpon>Settings.settings</DependentUpon>
6464
</Compile>
6565
<Compile Include="ToolWindow\TokenTrackerToolWindow.cs" />
66+
<Compile Include="Utilities\OutputLogger.cs" />
6667
<Compile Include="WebBridge\ThemedHtmlBuilder.cs" />
6768
<Compile Include="Properties\AssemblyInfo.cs" />
6869
</ItemGroup>
69-
7070
<!-- WPF XAML -->
7171
<ItemGroup>
7272
<Page Include="ToolWindow\TokenTrackerControl.xaml">
@@ -77,7 +77,6 @@
7777
<DependentUpon>TokenTrackerControl.xaml</DependentUpon>
7878
</Compile>
7979
</ItemGroup>
80-
8180
<!-- VSIX manifest -->
8281
<ItemGroup>
8382
<None Include="Properties\Settings.settings">
@@ -88,19 +87,16 @@
8887
<SubType>Designer</SubType>
8988
</None>
9089
</ItemGroup>
91-
9290
<!-- VSCT: VS Command Table definition (menus / commands) -->
9391
<ItemGroup>
9492
<VSCTCompile Include="CopilotTokenTrackerPackage.vsct">
9593
<ResourceName>Menus.ctmenu</ResourceName>
9694
</VSCTCompile>
9795
</ItemGroup>
98-
9996
<!-- Embedded resources -->
10097
<ItemGroup>
10198
<EmbeddedResource Include="WebBridge\vscode-shim.js" />
10299
</ItemGroup>
103-
104100
<!-- Framework references -->
105101
<ItemGroup>
106102
<Reference Include="System" />
@@ -111,7 +107,6 @@
111107
<Reference Include="WindowsBase" />
112108
<Reference Include="System.Xaml" />
113109
</ItemGroup>
114-
115110
<!-- NuGet packages -->
116111
<ItemGroup>
117112
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.6.36389" ExcludeAssets="runtime" NoWarn="NU1604" />
@@ -121,10 +116,8 @@
121116
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
122117
<PackageReference Include="System.Text.Json" Version="8.0.5" />
123118
</ItemGroup>
124-
125119
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
126120
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
127-
128121
<!--
129122
Copy webview JS bundles from the vscode-extension build output into this project's
130123
webview\ folder so they can be packaged inside the VSIX.
@@ -141,7 +134,6 @@
141134
<Copy SourceFiles="@(_WebviewBundle)" DestinationFolder="$(MSBuildProjectDirectory)\webview\" SkipUnchangedFiles="true" ContinueOnError="true" />
142135
<Message Importance="high" Text="Copied webview bundles from vscode-extension/dist/webview/ — rebuild vscode-extension if they are missing." />
143136
</Target>
144-
145137
<!-- Include copied webview JS files inside the VSIX package -->
146138
<ItemGroup>
147139
<Content Include="webview\*.js" Condition="Exists('$(MSBuildProjectDirectory)\webview')">
@@ -150,5 +142,4 @@
150142
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
151143
</Content>
152144
</ItemGroup>
153-
154-
</Project>
145+
</Project>

0 commit comments

Comments
 (0)