feat: add GitHub Copilot CLI as 7th supported agent#207
feat: add GitHub Copilot CLI as 7th supported agent#207ABIvan-Tech wants to merge 12 commits intovakovalskii:mainfrom
Conversation
- Reads VS Code sessions from ~/.copilot/session-state/<uuid>/ - Reads JetBrains sessions from ~/.copilot/jb/<uuid>/partition-1.jsonl - Parses workspace.yaml (flat + block scalar) for metadata - Active detection via inuse.<pid>.lock files - Wires into loadSessions(), loadSessionDetail(), findSessionFile() - Cache invalidation via COPILOT_SESSION_DIR mtime tracking - Badge: .badge-copilot and .tool-copilot styles - Detail: 'Open in VS Code' button instead of Focus Terminal - Adds openInVSCode() frontend function + vscode IDE handler Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…assistant turns Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR aims to add GitHub Copilot CLI as an additional supported agent by scanning Copilot session directories, surfacing them in the sessions list, and adding UI affordances to open related projects in VS Code.
Changes:
- Add Copilot session directory constants and new scan/detail loaders for Copilot sessions (VS Code/CLI + JetBrains) in
src/data.js. - Add a Copilot-specific “Open in VS Code” button in the session detail panel.
- Extend IDE-launch handling and update ignore patterns.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
src/server.js |
Adds additional IDE launch handling for Cursor/VS Code in /api/open-ide. |
src/frontend/detail.js |
Adds a Copilot-only launch button intended to open projects in VS Code. |
src/data.js |
Introduces Copilot CLI/JetBrains session scanning + detail parsing, and wires Copilot into session loading/detail logic. |
.gitignore |
Adds ignores for several directories (including .github/). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Copilot Chat (JSON/JSONL) | ||
| if (found.format === 'copilot-chat') { | ||
| >>>>>>> main | ||
| return loadCopilotDetail(sessionId); | ||
| } | ||
|
|
| // Load Copilot CLI sessions | ||
| try { | ||
| const copilotSessions = scanCopilotSessions(); | ||
| for (const cs of copilotSessions) sessions[cs.id] = cs; | ||
| } catch {} | ||
|
|
||
| // Try Copilot Chat (file-based, prefixed IDs) | ||
| if (sessionId.startsWith('copilot-')) { | ||
| const baseName = sessionId.replace(/^copilot-/, ''); | ||
| const wsMap = buildCopilotWorkspaceMap(); | ||
| for (const hash of Object.keys(wsMap)) { | ||
| const { chatDir } = wsMap[hash]; | ||
| for (const ext of ['.json', '.jsonl']) { | ||
| const candidate = path.join(chatDir, baseName + ext); | ||
| if (fs.existsSync(candidate)) return { file: candidate, format: 'copilot-chat' }; | ||
| } | ||
| // Load Kilo CLI sessions | ||
| try { | ||
| const kiloSessions = scanKiloCliSessions(); | ||
| for (const ks of kiloSessions) { | ||
| sessions[ks.id] = ks; | ||
| } | ||
| } | ||
| } catch {} | ||
|
|
||
| // Load Copilot Chat sessions | ||
| try { | ||
| const copilotSessions = scanCopilotSessions(); | ||
| for (const cs of copilotSessions) { | ||
| sessions[cs.id] = cs; | ||
| } | ||
| } catch {} |
| QWEN_DIR, | ||
| OPENCODE_DB, | ||
| KIRO_DB, | ||
| KIRO_DB, |
| if (ide === 'cursor') { | ||
| exec(`cursor "${target || '.'}"`); | ||
| } else if (ide === 'code' || ide === 'vscode') { | ||
| exec(`code "${target || '.'}"`); | ||
| } |
| } else if (s.tool === 'copilot') { | ||
| infoHtml += '<button class="launch-btn" style="background:#1f6feb" onclick="openInVSCode(\'' + escHtml(s.project || '') + '\')">Open in VS Code</button>'; |
| return result; | ||
| } | ||
|
|
||
| function scanCopilotSessions() { |
…n, remove redundant exec(), add openInVSCode
NovakPAai
left a comment
There was a problem hiding this comment.
Code Review + Security Review
Thanks for the effort on adding Copilot CLI support! The approach is solid and the May 1 fixes addressed the bot comments. However, I found 2 critical bugs that will crash at runtime, 1 regression that breaks existing agents, and 1 security issue. All need to be fixed before merge.
🔴 CRITICAL — Runtime crash: loadCopilotDetail is not defined
In loadSessionDetail() you call:
return loadCopilotDetail(sessionId);…and export loadCopilotDetail. But the actual function defined in this PR is named loadCopilotCliDetail.
loadCopilotDetail does not exist anywhere → ReferenceError every time a Copilot session detail is opened.
Fix: rename the function to loadCopilotDetail everywhere it is defined, or update the call/export to loadCopilotCliDetail.
🔴 CRITICAL — Regression: Kilo CLI and Copilot Chat sessions broken
findSessionFile() previously had two blocks that are deleted in this PR but never replaced:
// DELETED — Kilo CLI lookup
if (fs.existsSync(KILO_DB) && sessionId.startsWith('ses_')) { … }
// DELETED — Copilot Chat lookup
if (sessionId.startsWith('copilot-')) { … }loadSessionDetail() still routes format === 'kilo' and format === 'copilot-chat' through findSessionFile, but findSessionFile can no longer return those formats. Result: all existing Kilo and Copilot Chat session details return null / crash after this PR.
Fix: restore both removed blocks (they are not in conflict with the new Copilot CLI blocks).
🔴 SECURITY — Path traversal via unvalidated sessionId
In loadCopilotCliDetail() and findSessionFile():
const eventsPath = path.join(COPILOT_SESSION_DIR, sessionId, 'events.jsonl');sessionId comes from an HTTP request parameter. A value like ../../.env or ../../etc/passwd traverses outside ~/.copilot/session-state/. The UUID-length check (uuid.length !== 36) is only applied during scanning in scanCopilotCliSessions() — not in the lookup path.
Fix: validate sessionId against a strict UUID regex before using it in path construction:
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId)) {
return { messages: [] };
}🟡 MEDIUM — findSessionFile JB scan is O(N×file_size) on every detail request
For JetBrains sessions, findSessionFile reads and parses every partition file line-by-line to match a conversationId. This scan runs on every session detail open, with no caching. With many sessions this will be noticeably slow.
Suggestion: build the conversationId → uuid map once (lazily, same pattern as buildCopilotWorkspaceMap()) and cache it with an mtime guard.
🟡 MEDIUM — COPILOT_JB_DIR not tracked in cache invalidation
_sessionsNeedRescan() watches COPILOT_SESSION_DIR mtime, but not COPILOT_JB_DIR. New JetBrains sessions will not trigger a cache refresh until something else changes.
Fix: add the same statSync(COPILOT_JB_DIR) check in both _sessionsNeedRescan() and _updateScanMarkers().
🟡 MEDIUM — Broken indentation in loadSessions() disrupts readability
After the merge, several comment lines and the closing } catch {} for the Kiro block lost their leading spaces. The block is no longer consistently indented with the rest of the function. Not a runtime bug, but it signals a messy merge that should be cleaned up.
🟢 Minor — homedir vs ALL_HOMES[0] inconsistency in scanCopilotCliSessions
The VS Code section uses cwd.replace(homedir, '~') (local const homedir = ALL_HOMES[0]), but the JetBrains section uses cwd.replace(ALL_HOMES[0], '~') directly. Either use homedir consistently or remove the local variable.
Summary
| # | Severity | Issue |
|---|---|---|
| 1 | 🔴 Critical | loadCopilotDetail not defined → ReferenceError |
| 2 | 🔴 Critical | Kilo CLI + Copilot Chat lookup deleted from findSessionFile |
| 3 | 🔴 Security | Path traversal via unsanitized sessionId |
| 4 | 🟡 Medium | JB session lookup O(N) with no cache |
| 5 | 🟡 Medium | COPILOT_JB_DIR not watched in cache invalidation |
| 6 | 🟡 Medium | Indentation breakage in loadSessions() |
| 7 | 🟢 Minor | homedir / ALL_HOMES[0] inconsistency |
Issues 1–3 are blockers. 4–6 should be fixed in the same PR since the code is fresh. 7 can be a follow-up.
@ABIvan-Tech please address the blockers and force-push (or add a fixup commit) — happy to re-review quickly.
|
Hi @ABIvan-Tech, thanks again for the contribution! 🙏 We're holding off on merging until the three blockers from @NovakPAai's review are addressed:
The medium-priority items (4–6 in the review) would be great to fix in the same PR while the code is fresh, but they're not strictly blocking. Item 7 can be a follow-up. Once you push the fixes, ping us and we'll re-review quickly. Looking forward to landing this! |
…versal, cache tracking - Fix loadCopilotDetail vs loadCopilotCliDetail mismatch (runtime crash) - Add UUID validation in loadCopilotCliDetail to prevent path traversal - Add COPILOT_JB_DIR mtime tracking for cache invalidation
…rminal like other CLI agents
|
@vakovalskii pls check |
Summary
Changes
Testing