|
1 | 1 | # Copilot Instructions for copilot-handoff |
2 | 2 |
|
3 | 3 | ## Project Overview |
4 | | -This is a VS Code extension that tracks GitHub Copilot chat session duration and reminds developers to perform context-preserving handoffs when switching between Copilot agents or sessions. |
| 4 | +VS Code extension that tracks editor session duration and reminds developers to perform context-preserving handoffs. Helps prevent context degradation in long coding sessions by monitoring activity and providing structured context export. |
5 | 5 |
|
6 | | -## Architecture (To Be Implemented) |
| 6 | +**Core Purpose**: Proxy-track general editor activity as a signal for Copilot usage, then notify users when session duration exceeds thresholds to encourage fresh context starts. |
7 | 7 |
|
8 | | -### Core Components |
9 | | -- **Session Tracker**: Monitors active Copilot chat sessions and tracks duration |
10 | | -- **Notification System**: Alerts users when session approaches recommended handoff thresholds |
11 | | -- **Context Preservation**: Helps users export/save conversation context before switching agents |
12 | | -- **Settings Manager**: User-configurable thresholds and notification preferences |
| 8 | +## Architecture |
13 | 9 |
|
14 | | -### VS Code Extension Structure |
15 | | -Follow standard VS Code extension patterns: |
16 | | -- `package.json`: Extension manifest with contribution points (commands, settings, activation events) |
17 | | -- `src/extension.ts`: Entry point with `activate()` and `deactivate()` functions |
18 | | -- Use VS Code Extension API for Copilot integration and UI notifications |
| 10 | +### Component Structure (4 main files in src/) |
| 11 | +- **extension.ts**: Coordinator - registers 4 commands, creates status bar, starts tracking/monitoring, handles 1-minute UI refresh |
| 12 | +- **sessionTracker.ts**: Activity monitor - listens to editor events (`onDidChangeTextDocument`, `onDidChangeActiveTextEditor`, `onDidChangeWindowState`), auto-resets after 5-min inactivity |
| 13 | +- **notificationManager.ts**: Notification scheduler - polls every 60 seconds, implements "once" vs "periodic" logic, persists notification state |
| 14 | +- **contextExporter.ts**: Markdown generator - builds structured handoff documents with workspace metadata, offers 3 export modes (clipboard/file/new doc) |
19 | 15 |
|
20 | | -## Development Setup |
| 16 | +### Data Flow |
| 17 | +``` |
| 18 | +Editor Event → SessionTracker.recordActivity() → updates lastActivityTime |
| 19 | + ↓ |
| 20 | + globalState.set() |
| 21 | + |
| 22 | +NotificationManager (60s poll) → reads duration → checks threshold → shows notification |
| 23 | + ↓ |
| 24 | +User clicks "Export Context" → ContextExporter.exportContext() → QuickPick → generates markdown |
| 25 | +``` |
| 26 | + |
| 27 | +### State Persistence (globalState keys) |
| 28 | +All state lives in `ExtensionContext.globalState` (survives restarts, global across workspaces): |
| 29 | +- `sessionStartTime` - Session start timestamp |
| 30 | +- `lastActivityTime` - Last recorded activity (used for 5-min auto-reset) |
| 31 | +- `lastNotificationTime` - Last notification timestamp (for periodic intervals) |
| 32 | +- `hasShownInitialNotification` - Boolean flag for "once" mode |
| 33 | + |
| 34 | +**Critical Pattern**: Each component reads state in constructor, writes via private `saveState()` methods. No in-memory-only state. |
| 35 | + |
| 36 | +### Key Design Decisions |
| 37 | +- **Activity proxy tracking**: No direct Copilot Chat API exists, so tracks ANY editor activity (text changes, window focus, editor switches) as proxy for Copilot usage |
| 38 | +- **Polling architecture**: Uses `setInterval()` for status bar (60s) and notifications (60s) instead of reactive events - simpler but 1-minute granularity |
| 39 | +- **5-minute inactivity timeout**: If `Date.now() - lastActivityTime > 5min`, auto-resets session to prevent stale tracking after breaks |
| 40 | +- **Global state scope**: Extension state is per VS Code instance, NOT per workspace (sessionStartTime shared across all folders) |
| 41 | + |
| 42 | +## Development Workflow |
| 43 | + |
| 44 | +### Build & Debug |
| 45 | +```bash |
| 46 | +npm install # Install deps (requires Node 20+) |
| 47 | +npm run compile # TypeScript → out/ (one-time build) |
| 48 | +npm run watch # Watch mode (rebuilds on save) |
| 49 | +F5 # Launch Extension Development Host in new VS Code window |
| 50 | +``` |
| 51 | + |
| 52 | +**F5 Debugging Setup**: VS Code auto-creates `Extension Host` debug config. Set breakpoints in TypeScript source (sourcemaps enabled). Check Debug Console for `console.log()` output from extension. |
| 53 | + |
| 54 | +### Testing |
| 55 | +```bash |
| 56 | +npm test # Runs: compile + lint + launches test instance |
| 57 | +``` |
21 | 58 |
|
22 | | -### Prerequisites |
23 | | -- Node.js (LTS version recommended) |
24 | | -- VS Code with Extension Development Host capabilities |
25 | | -- TypeScript for type safety |
| 59 | +**Test Structure**: Integration tests in `src/test/suite/extension.test.ts` using Mocha + `@vscode/test-electron`. Tests verify: |
| 60 | +- Extension activation (`vscode.extensions.getExtension()`) |
| 61 | +- Command registration (`vscode.commands.getCommands()`) |
| 62 | +- Config defaults (`vscode.workspace.getConfiguration()`) |
26 | 63 |
|
27 | | -### Key Commands (To Be Defined) |
28 | | -- `npm install`: Install dependencies |
29 | | -- `npm run compile`: Build TypeScript |
30 | | -- `npm run watch`: Rebuild on changes |
31 | | -- `F5` in VS Code: Launch Extension Development Host for testing |
| 64 | +**Manual Testing Required**: Chat participant features (@handoff) need manual verification. See [TESTING.md](../TESTING.md) for checklist. |
32 | 65 |
|
33 | | -## VS Code Extension Best Practices |
| 66 | +### Linting & Packaging |
| 67 | +```bash |
| 68 | +npm run lint # ESLint with TypeScript parser (@typescript-eslint) |
| 69 | +npm run package # Creates .vsix (requires Node 20+) |
| 70 | +``` |
| 71 | + |
| 72 | +**Note**: `package` and `publish` scripts require Node 20+ and use `node node_modules/@vscode/vsce/vsce` to avoid global install. |
34 | 73 |
|
35 | | -### Activation Events |
36 | | -- Use `onStartupFinished` or Copilot-specific activation events to minimize startup impact |
37 | | -- Consider `onLanguage`, `onCommand`, or custom activation events based on when tracking should begin |
| 74 | +## Configuration (package.json contributes.configuration) |
38 | 75 |
|
39 | | -### Copilot Integration |
40 | | -- Monitor VS Code's Chat API for active sessions |
41 | | -- Use `vscode.chat` namespace APIs when available |
42 | | -- Track chat panel focus/blur events to determine active session duration |
| 76 | +All settings under `copilot-handoff.*` namespace with validation: |
| 77 | +- `sessionThresholdMinutes` (5-180, default 30) - When to show first reminder |
| 78 | +- `notificationFrequency` ("once" | "periodic" | "never", default "periodic") - Reminder behavior |
| 79 | +- `periodicReminderMinutes` (1-60, default 10) - Interval for periodic mode |
| 80 | +- `autoExportContext` (boolean, default false) - **Not implemented** - reserved for future auto-export |
| 81 | +- `showStatusBar` (boolean, default true) - Show/hide clock icon in status bar |
| 82 | +- `trackingEnabled` (boolean, default true) - Master toggle for all tracking |
43 | 83 |
|
44 | | -### User Experience |
45 | | -- Show status bar item with session duration |
46 | | -- Use non-intrusive notifications (information messages, not warnings) |
47 | | -- Allow users to dismiss or snooze handoff reminders |
48 | | -- Persist session state across VS Code window reloads |
| 84 | +**Reading Config**: Always use `vscode.workspace.getConfiguration('copilot-handoff').get<Type>('key', defaultValue)` with explicit defaults for type safety. |
49 | 85 |
|
50 | | -## Configuration |
| 86 | +## Project-Specific Patterns |
51 | 87 |
|
52 | | -### Expected Settings (in `package.json` contributions) |
| 88 | +### Command Registration (4 commands) |
| 89 | +All follow `copilot-handoff.<action>` naming in `extension.ts activate()`: |
| 90 | +```typescript |
| 91 | +vscode.commands.registerCommand('copilot-handoff.showSessionInfo', () => {...}) |
| 92 | +``` |
| 93 | +Always push to `context.subscriptions` for cleanup. Commands: |
| 94 | +- `showSessionInfo` - Modal with duration, threshold, action buttons |
| 95 | +- `exportContext` - Async QuickPick → 3 export options |
| 96 | +- `resetSession` - Calls `sessionTracker.resetSession()`, shows info message |
| 97 | +- `toggleTracking` - Flips `trackingEnabled` config value |
| 98 | + |
| 99 | +### Event Listeners (sessionTracker.ts) |
| 100 | +Activity tracking via 4 event types in `startTracking()`: |
53 | 101 | ```typescript |
54 | | -"copilot-handoff.sessionThresholdMinutes": number // When to remind (default: 30) |
55 | | -"copilot-handoff.notificationFrequency": string // "once" | "periodic" | "never" |
56 | | -"copilot-handoff.autoExportContext": boolean // Auto-save context on handoff |
| 102 | +vscode.workspace.onDidChangeTextDocument(() => this.recordActivity()) |
| 103 | +vscode.window.onDidChangeActiveTextEditor(() => this.recordActivity()) |
| 104 | +vscode.window.onDidChangeWindowState(state => {...}) |
| 105 | +vscode.commands.registerCommand('workbench.panel.chat.view.copilot.focus', ...) |
57 | 106 | ``` |
| 107 | +All listeners pushed to `this.listeners[]` array, disposed in `dispose()`. |
58 | 108 |
|
59 | | -## Testing Strategy |
60 | | -- Unit tests for session tracking logic |
61 | | -- Integration tests using VS Code Extension Test Runner |
62 | | -- Manual testing in Extension Development Host |
63 | | -- Test with real Copilot chat sessions for accurate duration tracking |
64 | | - |
65 | | -## Project Conventions |
66 | | -- Use TypeScript strict mode for type safety |
67 | | -- Follow VS Code extension naming conventions for commands (`copilot-handoff.commandName`) |
68 | | -- Store minimal state; prefer VS Code's built-in storage APIs (globalState/workspaceState) |
69 | | -- MIT License - keep copyright notice in new files |
70 | | - |
71 | | -## Known Constraints |
72 | | -- Extension depends on GitHub Copilot being installed and active |
73 | | -- Chat API availability may vary by VS Code version |
74 | | -- Session tracking should not impact performance or interrupt workflows |
| 109 | +### Status Bar Pattern |
| 110 | +**Non-reactive updates**: Uses `setInterval()` instead of listening to state changes: |
| 111 | +```typescript |
| 112 | +setInterval(() => updateStatusBar(), 60000) // Every minute |
| 113 | +``` |
| 114 | +Format: `$(clock) Xh Ym` or `$(clock) Ym`, tooltip shows "Click for details". Hide if `trackingEnabled=false` or `showStatusBar=false`. |
| 115 | + |
| 116 | +### Context Export Template Structure |
| 117 | +`ContextExporter.generateContextSummary()` builds markdown with: |
| 118 | +1. Metadata block: timestamp, workspace name, current file |
| 119 | +2. Auto-detected context: active selection, language, open editors |
| 120 | +3. Manual fill-in sections: "What I Was Working On", "Key Decisions Made", "Next Steps" |
| 121 | + |
| 122 | +**Export modes**: |
| 123 | +- Clipboard: Direct `vscode.env.clipboard.writeText()` |
| 124 | +- Save to File: `vscode.window.showSaveDialog()` → `fs.writeFileSync()` |
| 125 | +- New Document: `vscode.workspace.openTextDocument({ content, language: 'markdown' })` |
| 126 | + |
| 127 | +## TypeScript Configuration (tsconfig.json) |
| 128 | + |
| 129 | +- **Target**: ES2020 (supports Node.js 20+ features like optional chaining) |
| 130 | +- **Module**: CommonJS (VS Code extension requirement) |
| 131 | +- **Strict mode**: Enabled (null checks, strict function types, no implicit any) |
| 132 | +- **Output**: `out/` directory (gitignored, created on compile) |
| 133 | +- **Exclude**: `node_modules`, `.vscode-test`, `src/test` (tests run via separate test runner) |
| 134 | + |
| 135 | +**Source maps enabled**: Set breakpoints in `.ts` files, not compiled `.js`. |
| 136 | + |
| 137 | +## Known Limitations & Future Work |
| 138 | + |
| 139 | +- **No direct Copilot Chat API**: Tracks general editor activity as proxy (may trigger when not using Copilot) |
| 140 | +- **Global state scope**: Session state shared across all workspace folders (not workspace-specific) |
| 141 | +- **Notification granularity**: 1-minute polling intervals (not real-time) |
| 142 | +- **Auto-export placeholder**: `autoExportContext` setting exists but feature not implemented |
| 143 | +- **Manual handoff required**: No automated context export or Copilot Chat integration |
| 144 | + |
| 145 | +## Extending the Extension |
| 146 | + |
| 147 | +### Adding a New Command |
| 148 | +1. Add to `package.json` → `contributes.commands` with `command` and `title` |
| 149 | +2. Register in `extension.ts` `activate()`: |
| 150 | + ```typescript |
| 151 | + context.subscriptions.push( |
| 152 | + vscode.commands.registerCommand('copilot-handoff.myCommand', async () => {...}) |
| 153 | + ) |
| 154 | + ``` |
| 155 | +3. Update README.md command list and quickstart guide |
| 156 | + |
| 157 | +### Adding a New Configuration |
| 158 | +1. Add to `package.json` → `contributes.configuration.properties` with `type`, `default`, `description`, optional `enum`/`minimum`/`maximum` |
| 159 | +2. Read in code: |
| 160 | + ```typescript |
| 161 | + const config = vscode.workspace.getConfiguration('copilot-handoff'); |
| 162 | + const value = config.get<Type>('myKey', defaultValue); |
| 163 | + ``` |
| 164 | +3. For reactive behavior: Listen to `vscode.workspace.onDidChangeConfiguration(e => { if (e.affectsConfiguration('copilot-handoff.myKey')) {...} })` |
| 165 | + |
| 166 | +### Modifying Session Tracking Logic |
| 167 | +Edit `SessionTracker.startTracking()` to add/remove event listeners. Common patterns: |
| 168 | +- Add listener: `this.listeners.push(vscode.workspace.onXxx(() => this.recordActivity()))` |
| 169 | +- Change inactivity timeout: Modify `ACTIVITY_TIMEOUT` constant (currently 5 * 60 * 1000 ms) |
| 170 | +- Custom activity logic: Override `recordActivity()` with conditional checks before `this.lastActivityTime = Date.now()` |
| 171 | + |
| 172 | +### Testing Notification Timing |
| 173 | +For manual testing, temporarily reduce thresholds: |
| 174 | +```json |
| 175 | +{ |
| 176 | + "copilot-handoff.sessionThresholdMinutes": 1, |
| 177 | + "copilot-handoff.periodicReminderMinutes": 1 |
| 178 | +} |
| 179 | +``` |
| 180 | +Wait 1-2 minutes to trigger notifications. See [TESTING.md](../TESTING.md) for full checklist. |
0 commit comments