Skip to content

Commit c558ebe

Browse files
fix: add prompt size safeguard to prevent Copilot context overflow
1 parent b2f75d0 commit c558ebe

5 files changed

Lines changed: 107 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to the "promptitude" extension will be documented in this fi
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- Prompt size safeguard: warns when active prompts may overwhelm GitHub Copilot's context window. Checks total file size (>500 KB), active prompt count (>50), and repository count (>10) after each sync. Shows actionable warning with "Manage Prompts" and "Open Settings" buttons.
10+
- Disabled (inactive) prompts are now cleaned up from the active prompts directory during sync, preventing them from being loaded by Copilot.
11+
712
## [1.5.4] - 2026-01-12
813

914
### Fixed

src/constant.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,11 @@ export const REPO_SYNC_CHAT_MODE_LEGACY_PATH = `chatmodes/`;
44
export const REPO_SYNC_CHAT_MODE_LEGACY_SINGULAR_PATH = `chatmode/`;
55
export const REPO_SYNC_INSTRUCTIONS_PATH = `instructions/`;
66
export const REPO_SYNC_PROMPT_PATH = `prompts/`;
7+
8+
// Prompt size safeguard thresholds
9+
/** Maximum total size (in bytes) of all active prompts before warning the user */
10+
export const PROMPT_SIZE_WARNING_THRESHOLD_BYTES = 500 * 1024; // 500 KB
11+
/** Maximum number of active prompts before warning the user */
12+
export const PROMPT_COUNT_WARNING_THRESHOLD = 50;
13+
/** Maximum number of configured repositories before warning the user */
14+
export const REPO_COUNT_WARNING_THRESHOLD = 10;

src/syncManager.ts

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { GitProviderFactory } from './utils/gitProviderFactory';
1010
import { FileSystemManager } from './utils/fileSystem';
1111
import { AzureDevOpsApiManager } from './utils/azureDevOps';
1212
import { PromptTreeDataProvider } from './ui/promptTreeProvider';
13-
import { REPO_SYNC_CHAT_MODE_PATH, REPO_SYNC_CHAT_MODE_LEGACY_PATH, REPO_SYNC_CHAT_MODE_LEGACY_SINGULAR_PATH, REPO_SYNC_INSTRUCTIONS_PATH, REPO_SYNC_PROMPT_PATH, } from './constant';
13+
import { REPO_SYNC_CHAT_MODE_PATH, REPO_SYNC_CHAT_MODE_LEGACY_PATH, REPO_SYNC_CHAT_MODE_LEGACY_SINGULAR_PATH, REPO_SYNC_INSTRUCTIONS_PATH, REPO_SYNC_PROMPT_PATH, PROMPT_SIZE_WARNING_THRESHOLD_BYTES, PROMPT_COUNT_WARNING_THRESHOLD, REPO_COUNT_WARNING_THRESHOLD, } from './constant';
1414
export interface SyncResult {
1515
success: boolean;
1616
itemsUpdated: number;
@@ -102,12 +102,18 @@ export class SyncManager {
102102
// Recreate symlinks for active prompts (in case they were manually deleted)
103103
await this.recreateActivePromptSymlinks();
104104

105+
// Remove disabled prompts from the active prompts directory
106+
await this.cleanupInactivePrompts();
107+
105108
// Clean up orphaned regular files in prompts directory
106109
const cleanup = await this.cleanupOrphanedPrompts();
107110
if (cleanup.removed > 0) {
108111
this.logger.info(`Cleaned up ${cleanup.removed} orphaned prompt files`);
109112
}
110113

114+
// Validate total prompt size to prevent Copilot context overflow
115+
await this.validatePromptSize();
116+
111117
// Update status based on overall result
112118
if (result.overallSuccess) {
113119
this.statusBar.setStatus(SyncStatus.Success);
@@ -289,7 +295,8 @@ export class SyncManager {
289295
}
290296

291297
const filePath = this.fileSystem.joinPath(promptsDir, fileName);
292-
const matchingPrompt = allPrompts.find(prompt => prompt.name === fileName);
298+
// Match on both prompt.name and prompt.workspaceName to handle disambiguated files (e.g., prompt@org-repo.md)
299+
const matchingPrompt = allPrompts.find(prompt => prompt.name === fileName || prompt.workspaceName === fileName);
293300

294301
// Remove file if prompt exists but is not active
295302
if (matchingPrompt && !matchingPrompt.active) {
@@ -1226,6 +1233,64 @@ export class SyncManager {
12261233
}
12271234
}
12281235

1236+
/**
1237+
* Validate total prompt size and count, warning the user if thresholds are exceeded.
1238+
* This prevents GitHub Copilot's context window from being overwhelmed.
1239+
*/
1240+
private async validatePromptSize(): Promise<void> {
1241+
try {
1242+
const promptsDir = this.config.getPromptsDirectory();
1243+
1244+
// Ensure the prompts directory exists
1245+
await this.fileSystem.ensureDirectoryExists(promptsDir);
1246+
1247+
const entries = await this.fileSystem.readDirectory(promptsDir);
1248+
const promptFiles = entries.filter(f => this.isPromptFile(f));
1249+
1250+
// Calculate total size of active prompt files
1251+
let totalSize = 0;
1252+
let activeCount = 0;
1253+
1254+
for (const file of promptFiles) {
1255+
try {
1256+
const filePath = this.fileSystem.joinPath(promptsDir, file);
1257+
totalSize += await this.fileSystem.getFileSize(filePath);
1258+
activeCount++;
1259+
} catch {
1260+
// Skip files that can't be stat'd
1261+
}
1262+
}
1263+
1264+
const repoCount = this.config.repositories.length;
1265+
const totalSizeKB = totalSize / 1024;
1266+
const reasons: string[] = [];
1267+
1268+
if (totalSize > PROMPT_SIZE_WARNING_THRESHOLD_BYTES) {
1269+
reasons.push(`total size (${totalSizeKB.toFixed(0)} KB) exceeds ${(PROMPT_SIZE_WARNING_THRESHOLD_BYTES / 1024).toFixed(0)} KB`);
1270+
}
1271+
if (activeCount > PROMPT_COUNT_WARNING_THRESHOLD) {
1272+
reasons.push(`${activeCount} active prompts exceeds limit of ${PROMPT_COUNT_WARNING_THRESHOLD}`);
1273+
}
1274+
if (repoCount > REPO_COUNT_WARNING_THRESHOLD) {
1275+
reasons.push(`${repoCount} repositories exceeds limit of ${REPO_COUNT_WARNING_THRESHOLD}`);
1276+
}
1277+
1278+
if (reasons.length > 0) {
1279+
this.logger.warn(`Prompt size safeguard triggered: ${reasons.join('; ')}`);
1280+
await this.notifications.showPromptSizeWarning({
1281+
totalSizeKB,
1282+
activeCount,
1283+
repoCount,
1284+
reasons
1285+
});
1286+
} else {
1287+
this.logger.debug(`Prompt size check passed: ${activeCount} prompts, ${totalSizeKB.toFixed(1)} KB, ${repoCount} repos`);
1288+
}
1289+
} catch (error) {
1290+
this.logger.error('Failed to validate prompt size', error instanceof Error ? error : undefined);
1291+
}
1292+
}
1293+
12291294
dispose(): void {
12301295
this.logger.info('Disposing SyncManager...');
12311296

src/utils/fileSystem.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ export class FileSystemManager {
5858
}
5959
}
6060

61+
async getFileSize(filePath: string): Promise<number> {
62+
const stats = await stat(filePath);
63+
return stats.size;
64+
}
6165

6266
joinPath(...paths: string[]): string {
6367
return path.join(...paths);

src/utils/notifications.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,27 @@ export class NotificationManager {
162162
await this.showError(`Failed to setup Azure DevOps authentication: ${errorMessage}`);
163163
}
164164
}
165+
166+
/**
167+
* Show a warning when the total prompt size or count exceeds safe thresholds.
168+
* Always shown regardless of showNotifications setting to prevent Copilot context overflow.
169+
*/
170+
async showPromptSizeWarning(details: { totalSizeKB: number; activeCount: number; repoCount: number; reasons: string[] }): Promise<void> {
171+
const reasonText = details.reasons.join('; ');
172+
const message = `⚠️ Promptitude: Your active prompts may be too large for GitHub Copilot (${details.totalSizeKB.toFixed(0)} KB across ${details.activeCount} prompts from ${details.repoCount} repos). ${reasonText}. Consider disabling unused prompts, reducing synced repositories, or switching to manual sync.`;
173+
174+
// Always show this warning regardless of notification settings — it's a safeguard
175+
const result = await vscode.window.showWarningMessage(
176+
message,
177+
'Manage Prompts',
178+
'Open Settings',
179+
'Dismiss'
180+
);
181+
182+
if (result === 'Manage Prompts') {
183+
vscode.commands.executeCommand('promptitude.cards.focus');
184+
} else if (result === 'Open Settings') {
185+
vscode.commands.executeCommand('workbench.action.openSettings', 'promptitude');
186+
}
187+
}
165188
}

0 commit comments

Comments
 (0)