Skip to content
Open
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to the "promptitude" extension will be documented in this fi

## [Unreleased]

### Improved

- Sync failure messages now distinguish **invalid repository URLs** (e.g. sub-paths like `/tree/main/...`) from **incompatible repository structures** (repo is reachable but has no supported prompt folders). The summary toast shows a categorised count, and "Show Details" lists the specific reason per repository.

## [1.5.4] - 2026-01-12

### Fixed
Expand Down
47 changes: 44 additions & 3 deletions src/syncManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,27 @@ export class SyncManager {
}
}

/**
* Validates that a repository URL is a well-formed repo root (not a sub-path like /tree/main/...).
* Returns an error string if invalid, or undefined if valid.
*/
private validateRepositoryUrl(repoUrl: string): string | undefined {
try {
const parsed = new URL(repoUrl);
const normalizedPath = parsed.pathname.replace(/\.git$/, '').replace(/\/+$/, '');
const segments = normalizedPath.split('/').filter(Boolean);

// GitHub: expect exactly owner/repo (2 segments)
if (parsed.hostname === 'github.com' && segments.length !== 2) {
return `Invalid URL – expected https://github.com/owner/repo but got a sub-path (${parsed.pathname}). Remove extra path segments like /tree/…`;
}

return undefined;
} catch {
return `Invalid URL format – could not parse "${repoUrl}" as a repository URL`;
}
}

private async syncMultipleRepositories(repositories: string[]): Promise<MultiRepositorySyncResult> {
const results: RepositorySyncResult[] = [];
let totalItemsUpdated = 0;
Expand All @@ -338,6 +359,20 @@ export class SyncManager {
try {
this.logger.debug(`Syncing repository: ${repoUrl}`);

// Early validation: reject malformed repository URLs
const urlError = this.validateRepositoryUrl(repoUrl);
if (urlError) {
this.logger.warn(`Skipping repository ${repoUrl}: ${urlError}`);
results.push({
repository: repoUrl,
success: false,
itemsUpdated: 0,
error: urlError
});
errors.push(`${repoUrl}: ${urlError}`);
continue;
}

// Get or create Git API manager for this repository
let gitApi = this.gitProviders.get(repoUrl);
if (!gitApi) {
Expand Down Expand Up @@ -379,14 +414,20 @@ export class SyncManager {

if (relevantFiles.length === 0) {
this.logger.warn(`No relevant files found to sync in ${repoUrl} based on current settings`);
const promptLocation = `${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}`;
const enabledTypes: string[] = [];
if (this.config.syncChatmode) { enabledTypes.push(REPO_SYNC_CHAT_MODE_PATH); }
if (this.config.syncInstructions) { enabledTypes.push(REPO_SYNC_INSTRUCTIONS_PATH); }
if (this.config.syncPrompt) { enabledTypes.push(REPO_SYNC_PROMPT_PATH); }
const structureError = enabledTypes.length > 0
? `Incompatible repository – no .md/.txt files found under ${enabledTypes.join(', ')} on branch "${branch}". Check that the repo uses a supported folder layout.`
: `No sync types enabled – enable at least one of syncChatmode, syncInstructions, or syncPrompt in settings.`;
results.push({
repository: repoUrl,
success: false,
itemsUpdated: 0,
error: `No relevant files found, make sure prompts are in valid directories: ${promptLocation}`
error: structureError
});
errors.push(`${repoUrl}: No relevant files found`);
errors.push(`${repoUrl}: ${structureError}`);
continue;
}
this.logger.debug(`Found ${relevantFiles.length} relevant files to sync for ${repoUrl}`);
Expand Down
22 changes: 21 additions & 1 deletion src/utils/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,27 @@ export class NotificationManager {
}

async showPartialSyncSuccess(itemsCount: number, successCount: number, totalCount: number, errors: string[]): Promise<void> {
const message = `⚠️ Partial sync completed! ${itemsCount} items updated from ${successCount}/${totalCount} repositories.`;
// Categorise failures so the summary toast is immediately actionable
let badUrlCount = 0;
let badStructureCount = 0;
let otherCount = 0;
for (const err of errors) {
if (err.includes('Invalid URL')) {
badUrlCount++;
} else if (err.includes('Incompatible repository') || err.includes('No sync types enabled')) {
badStructureCount++;
} else {
otherCount++;
}
}

const failParts: string[] = [];
if (badUrlCount > 0) { failParts.push(`${badUrlCount} bad URL${badUrlCount > 1 ? 's' : ''}`); }
if (badStructureCount > 0) { failParts.push(`${badStructureCount} incompatible structure`); }
if (otherCount > 0) { failParts.push(`${otherCount} other error${otherCount > 1 ? 's' : ''}`); }

const failSummary = failParts.length > 0 ? ` Failures: ${failParts.join(', ')}.` : '';
const message = `⚠️ Partial sync: ${itemsCount} items updated from ${successCount}/${totalCount} repos.${failSummary}`;
Comment on lines +50 to +70
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor pluralization inconsistency for "incompatible structure".

The pluralization for badStructureCount doesn't add an 's' when count > 1, unlike the other categories.

✏️ Proposed fix
         if (badUrlCount > 0) { failParts.push(`${badUrlCount} bad URL${badUrlCount > 1 ? 's' : ''}`); }
-        if (badStructureCount > 0) { failParts.push(`${badStructureCount} incompatible structure`); }
+        if (badStructureCount > 0) { failParts.push(`${badStructureCount} incompatible structure${badStructureCount > 1 ? 's' : ''}`); }
         if (otherCount > 0) { failParts.push(`${otherCount} other error${otherCount > 1 ? 's' : ''}`); }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/notifications.ts` around lines 50 - 70, The pluralization for the
badStructureCount category is inconsistent: update the failParts push for
badStructureCount in src/utils/notifications.ts (the block that builds failParts
using badUrlCount, badStructureCount, otherCount) to conditionally append an "s"
when badStructureCount > 1 (e.g., use `${badStructureCount} incompatible
structure${badStructureCount > 1 ? 's' : ''}`) so it matches the pluralization
pattern used for badUrlCount and otherCount and keeps the failSummary/message
strings correct.

const result = await this.showWarning(
message,
'Show Details',
Expand Down
Loading