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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to the "promptitude" extension will be documented in this file.

## [1.5.6] - 2026-03-23

### Added
Comment thread
mohamedsedkiyouzbechi marked this conversation as resolved.

- Instructions size safeguard: warns when active instructions (`.instructions.md`) may overwhelm GitHub Copilot's context window. Checks total instructions file size (>500 KB), active instructions count (>50), and repository count (>10) after each sync. Only instructions are counted since they are auto-loaded into context, unlike `.prompt.md` files which are invoked on-demand.
- Disabled (inactive) prompts are now cleaned up from the active prompts directory during sync, preventing them from being loaded by Copilot.

## [1.5.5] - 2026-03-18

### Improved
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "promptitude-extension",
"displayName": "Promptitude",
"description": "Sync GitHub Copilot prompts, chatmodes and instructions from git repositories",
"version": "1.5.5",
"version": "1.5.6",
"publisher": "logientnventive",
"icon": "resources/promptitude-icon.png",
"repository": {
Expand Down
6 changes: 6 additions & 0 deletions src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ export const REPO_SYNC_CHAT_MODE_LEGACY_PATH = `chatmodes/`;
export const REPO_SYNC_CHAT_MODE_LEGACY_SINGULAR_PATH = `chatmode/`;
export const REPO_SYNC_INSTRUCTIONS_PATH = `instructions/`;
export const REPO_SYNC_PROMPT_PATH = `prompts/`;

// Instructions size safeguard thresholds
/** Maximum total size (in bytes) of all active instructions before warning the user */
export const INSTRUCTION_SIZE_WARNING_THRESHOLD_BYTES = 500 * 1024; // 500 KB
/** Maximum number of active instructions before warning the user */
export const INSTRUCTION_COUNT_WARNING_THRESHOLD = 50;
65 changes: 63 additions & 2 deletions src/syncManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { GitProviderFactory } from './utils/gitProviderFactory';
import { FileSystemManager } from './utils/fileSystem';
import { AzureDevOpsApiManager } from './utils/azureDevOps';
import { PromptTreeDataProvider } from './ui/promptTreeProvider';
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';
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, INSTRUCTION_SIZE_WARNING_THRESHOLD_BYTES, INSTRUCTION_COUNT_WARNING_THRESHOLD, } from './constant';
Comment thread
mohamedsedkiyouzbechi marked this conversation as resolved.
export interface SyncResult {
success: boolean;
itemsUpdated: number;
Expand Down Expand Up @@ -102,12 +102,18 @@ export class SyncManager {
// Recreate symlinks for active prompts (in case they were manually deleted)
await this.recreateActivePromptSymlinks();

// Remove disabled prompts from the active prompts directory
await this.cleanupInactivePrompts();

Comment thread
mohamedsedkiyouzbechi marked this conversation as resolved.
// Clean up orphaned regular files in prompts directory
const cleanup = await this.cleanupOrphanedPrompts();
if (cleanup.removed > 0) {
this.logger.info(`Cleaned up ${cleanup.removed} orphaned prompt files`);
}

// Validate total instruction size to prevent Copilot context overflow
await this.validateInstructionSize();

// Update status based on overall result
if (result.overallSuccess) {
this.statusBar.setStatus(SyncStatus.Success);
Expand Down Expand Up @@ -289,7 +295,8 @@ export class SyncManager {
}

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

// Remove file if prompt exists but is not active
if (matchingPrompt && !matchingPrompt.active) {
Expand Down Expand Up @@ -1290,6 +1297,60 @@ export class SyncManager {
}
}

/**
* Validate total instructions size and count, warning the user if thresholds are exceeded.
* Instructions (.instructions.md) are automatically loaded into Copilot's context window,
* unlike .prompt.md files which are invoked on-demand.
*/
private async validateInstructionSize(): Promise<void> {
try {
const promptsDir = this.config.getPromptsDirectory();

// Ensure the prompts directory exists
await this.fileSystem.ensureDirectoryExists(promptsDir);

const entries = await this.fileSystem.readDirectory(promptsDir);
// Only count instructions files — they are auto-loaded into Copilot's context
const instructionFiles = entries.filter(f => f.endsWith('.instructions.md'));
const activeCount = instructionFiles.length;

// Calculate total size of active instruction files
let totalSize = 0;

for (const file of instructionFiles) {
try {
const filePath = this.fileSystem.joinPath(promptsDir, file);
totalSize += await this.fileSystem.getFileSize(filePath);
} catch {
this.logger.debug(`Skipped unreadable instruction file: ${file}`);
}
}

Comment thread
mohamedsedkiyouzbechi marked this conversation as resolved.
const totalSizeKB = totalSize / 1024;
const reasons: string[] = [];

if (totalSize > INSTRUCTION_SIZE_WARNING_THRESHOLD_BYTES) {
reasons.push(`total size (${totalSizeKB.toFixed(0)} KB) exceeds ${(INSTRUCTION_SIZE_WARNING_THRESHOLD_BYTES / 1024).toFixed(0)} KB`);
}
if (activeCount > INSTRUCTION_COUNT_WARNING_THRESHOLD) {
reasons.push(`${activeCount} active instructions exceeds limit of ${INSTRUCTION_COUNT_WARNING_THRESHOLD}`);
}

if (reasons.length > 0) {
this.logger.warn(`Instructions size safeguard triggered: ${reasons.join('; ')}`);
await this.notifications.showPromptSizeWarning({
totalSizeKB,
activeCount,
reasons
});
} else {
this.logger.debug(`Instructions size check passed: ${activeCount} instructions, ${totalSizeKB.toFixed(1)} KB`);
}
} catch (error) {
this.logger.error('Failed to validate prompt size', error instanceof Error ? error : undefined);
}
}

dispose(): void {
this.logger.info('Disposing SyncManager...');

Expand Down
4 changes: 4 additions & 0 deletions src/utils/fileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export class FileSystemManager {
}
}

async getFileSize(filePath: string): Promise<number> {
const stats = await stat(filePath);
return stats.size;
}

joinPath(...paths: string[]): string {
return path.join(...paths);
Expand Down
23 changes: 23 additions & 0 deletions src/utils/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,27 @@ export class NotificationManager {
await this.showError(`Failed to setup Azure DevOps authentication: ${errorMessage}`);
}
}

/**
* Show a warning when the total instructions size or count exceeds safe thresholds.
* Instructions are auto-loaded into Copilot's context, unlike prompts which are on-demand.
*/
async showPromptSizeWarning(details: { totalSizeKB: number; activeCount: number; reasons: string[] }): Promise<void> {
const reasonText = details.reasons.join('; ');
const message = `⚠️ Promptitude: Your active instructions may be too large for GitHub Copilot's context window (${details.totalSizeKB.toFixed(0)} KB across ${details.activeCount} instructions). ${reasonText}. Consider disabling unused instructions.`;

// Always show this warning regardless of notification settings — it's a safeguard
const result = await vscode.window.showWarningMessage(
message,
'Manage Prompts',
'Open Settings',
'Dismiss'
);

if (result === 'Manage Prompts') {
vscode.commands.executeCommand('promptitude.cards.focus');
} else if (result === 'Open Settings') {
vscode.commands.executeCommand('workbench.action.openSettings', 'promptitude');
}
}
Comment thread
mohamedsedkiyouzbechi marked this conversation as resolved.
}
Loading