Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ Splitrail is a **fast, cross-platform, real-time token usage tracker and cost mo
- [Claude Code](https://github.com/anthropics/claude-code)
- [Codex CLI](https://github.com/openai/codex)
- [Cline](https://github.com/cline/cline) / [Roo Code](https://github.com/RooCodeInc/Roo-Code) / [Kilo Code](https://github.com/Kilo-Org/kilocode) (VS Code extension + CLI)
- [GitHub Copilot](https://github.com/features/copilot)
- [GitHub Copilot](https://github.com/features/copilot) (VS Code)
- [GitHub Copilot CLI](https://github.com/features/copilot)
- [OpenCode](https://github.com/sst/opencode)
- [Pi Agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent)

Expand Down
21 changes: 5 additions & 16 deletions src/analyzers/copilot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ struct CopilotToolCall {
}

// Helper function to count tokens in a string using tiktoken
fn count_tokens(text: &str) -> u64 {
pub(crate) fn count_tokens(text: &str) -> u64 {
// Use o200k_base encoding (GPT-4o and newer models)
match get_bpe_from_model("o200k_base") {
Ok(bpe) => {
Expand Down Expand Up @@ -189,13 +189,13 @@ fn extract_and_hash_project_id_copilot(_file_path: &Path) -> String {
hash_text("copilot-global")
}

fn is_probably_tool_json_text(text: &str) -> bool {
pub(crate) fn is_probably_tool_json_text(text: &str) -> bool {
let trimmed = text.trim_start();
(trimmed.starts_with('{') || trimmed.starts_with("[{")) && trimmed.contains("\"tool\"")
}

// Helper function to extract model from model_id field
fn extract_model_from_model_id(model_id: &str) -> Option<String> {
pub(crate) fn extract_model_from_model_id(model_id: &str) -> Option<String> {
// Model ID format examples:
// "generic-copilot/litellm/anthropic/claude-haiku-4.5"
// "LiteLLM/Sonnet 4.5"
Expand Down Expand Up @@ -425,22 +425,11 @@ impl Analyzer for CopilotAnalyzer {
fn get_data_glob_patterns(&self) -> Vec<String> {
let mut patterns = Vec::new();

// VSCode forks that might have Copilot installed: Code, Cursor, Windsurf, VSCodium, Positron, Antigravity
let vscode_forks = [
"Code",
"Cursor",
"Windsurf",
"VSCodium",
"Positron",
"Code - Insiders",
"Antigravity",
];

if let Some(home_dir) = dirs::home_dir() {
let home_str = home_dir.to_string_lossy();

// macOS paths for all VSCode forks
for fork in &vscode_forks {
for fork in COPILOT_VSCODE_FORKS {
patterns.push(format!("{home_str}/Library/Application Support/{fork}/User/workspaceStorage/*/chatSessions/*.json"));
}
}
Expand All @@ -449,7 +438,7 @@ impl Analyzer for CopilotAnalyzer {
}

fn discover_data_sources(&self) -> Result<Vec<DataSource>> {
let sources = Self::workspace_storage_dirs()
let sources: Vec<DataSource> = Self::workspace_storage_dirs()
.into_iter()
.flat_map(|dir| WalkDir::new(dir).min_depth(3).max_depth(3).into_iter())
.filter_map(|e| e.ok())
Expand Down
Loading
Loading