This document tracks GitHub Copilot CLI issues that we can solve with plugins.
Status: Plugin built and published
Plugin: AntiCompactionPlugin
Solution: github/copilot-cli#947 (comment)
Impact: HIGH - Users getting crashes on long sessions and model switching
Root Cause: K8l function only handles one direction of orphaned messages
Plugin Solution:
class MessageRepairPlugin {
name = 'message-repair';
async onBeforeSend(context, options) {
// Intercept before sending to API
// Clean up orphaned tool_calls and tool_results
const messages = options.messages || [];
// Step 1: Collect all tool_call IDs
const allToolCallIds = new Set();
messages.forEach(msg => {
if (msg.role === 'assistant' && msg.tool_calls) {
msg.tool_calls.forEach(tc => allToolCallIds.add(tc.id));
}
});
// Step 2: Filter out orphaned tool_results
const cleanedMessages = messages.filter(msg => {
if (msg.role === 'tool' && msg.tool_call_id) {
if (!allToolCallIds.has(msg.tool_call_id)) {
console.log(`β οΈ MessageRepairPlugin: Removing orphaned tool_result: ${msg.tool_call_id}`);
return false;
}
}
return true;
});
// Step 3: Find orphaned tool_calls
const seenResultIds = new Set(
cleanedMessages.filter(m => m.role === 'tool').map(m => m.tool_call_id)
);
const orphanedCallIds = [...allToolCallIds].filter(id => !seenResultIds.has(id));
// Step 4: Add fake results for orphaned tool_calls
if (orphanedCallIds.length > 0) {
console.log(`β οΈ MessageRepairPlugin: Adding ${orphanedCallIds.length} fake results for orphaned tool_calls`);
const fakeResults = orphanedCallIds.map(id => ({
role: 'tool',
tool_call_id: id,
content: 'The execution of this tool was interrupted.'
}));
cleanedMessages.push(...fakeResults);
}
return { ...options, messages: cleanedMessages };
}
}Why this works:
- Fixes bugs at SDK layer BEFORE they hit the API
- Works for ALL models (GPT, Claude, etc.)
- Handles both session resume AND model switching
- Community gets fix TODAY instead of waiting for GitHub
Next Steps:
- Build plugin
- Test with long sessions
- Test with model switching
- Comment on both issues with solution
Impact: MEDIUM - Developers can't reliably track actual sessions
Plugin Solution:
class SessionLifecyclePlugin {
name = 'session-lifecycle';
private actualSessionStart = null;
async onSessionCreated(context) {
// Track ACTUAL session start (only once)
if (!this.actualSessionStart) {
this.actualSessionStart = Date.now();
console.log('π΅ ACTUAL SESSION START');
await this.onActualSessionStart(context);
}
}
async onSessionEnd(context) {
// Only fire actual end on real session end
if (this.actualSessionStart) {
console.log('π΄ ACTUAL SESSION END');
const duration = Date.now() - this.actualSessionStart;
await this.onActualSessionEnd(context, duration);
this.actualSessionStart = null;
}
}
// User hooks
async onActualSessionStart(context) {
// User's code here
}
async onActualSessionEnd(context, duration) {
// User's code here
}
}Impact: MEDIUM - Users want to retry failed requests
Plugin Solution:
class RetryPlugin {
name = 'retry';
private lastRequest = null;
async onBeforeSend(context, options) {
// Check for /retry command
if (options.message === '/retry' && this.lastRequest) {
console.log('π Retrying last request...');
return this.lastRequest;
}
// Save for potential retry
this.lastRequest = { ...options };
return options;
}
async onAfterReceive(context, response) {
// If error, suggest retry
if (response.error) {
console.log('\nπ‘ TIP: Type /retry to retry the last request\n');
}
return response;
}
}Impact: LOW - Developers want more visibility
Plugin Solution:
class DebugLoggerPlugin {
name = 'debug-logger';
async onLoad() {
console.log('π Debug logging enabled');
}
async onSessionCreated(context) {
console.log('π Session created:', {
sessionId: context.sessionId,
model: context.model,
timestamp: new Date().toISOString()
});
}
async onBeforeSend(context, options) {
console.log('π Before send:', {
message: options.message?.substring(0, 100),
messageLength: options.message?.length,
hasTools: !!options.tools,
timestamp: new Date().toISOString()
});
return options;
}
async onAfterReceive(context, response) {
console.log('π After receive:', {
responseType: typeof response,
hasError: !!response.error,
timestamp: new Date().toISOString()
});
return response;
}
}- MessageRepairPlugin - Fixes critical crashes (issues #1005, #994)
- RetryPlugin - High user demand (#995)
- SessionLifecyclePlugin - Developer experience (#991)
- DebugLoggerPlugin - Developer experience (#993)
For each plugin:
- β Build and test locally
- β Add to built-in plugins
- β Update README
- β Publish new version to npm
- β Comment on GitHub issue with solution
- β Track adoption and feedback
- npm downloads - Track package adoption
- Issue engagement - Comments, reactions, +1s
- GitHub stars - Repo popularity
- Community plugins - Other developers building on our system
- GitHub team response - Acknowledgment or adoption
π΄ββ οΈ "Find pain, build solutions, ship fast." - Captain CP