From f9d991d4fdb11f26ba63aa57d2d58a4a68d5be87 Mon Sep 17 00:00:00 2001 From: harshitsinghbhandari <24b4506@iitb.ac.in> Date: Thu, 2 Apr 2026 19:19:44 +0530 Subject: [PATCH] fix: handle optional plugin field in CLI and web packages Update all usages of project.tracker.plugin and project.scm.plugin to use optional chaining since the plugin field is now optional when package or path fields are specified. Files updated: - CLI: doctor.ts, status.ts, verify.ts - Web: issues/route.ts, verify/route.ts, scm-webhooks.ts, serialize.ts, services.ts Co-Authored-By: Claude Opus 4.5 --- packages/cli/src/commands/doctor.ts | 4 +++- packages/cli/src/commands/status.ts | 2 +- packages/cli/src/commands/verify.ts | 4 ++++ packages/web/src/app/api/issues/route.ts | 4 ++-- packages/web/src/app/api/verify/route.ts | 2 +- packages/web/src/lib/scm-webhooks.ts | 2 +- packages/web/src/lib/serialize.ts | 4 ++-- packages/web/src/lib/services.ts | 12 ++++++------ 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/commands/doctor.ts b/packages/cli/src/commands/doctor.ts index d0e7fcc07..5bdaa0dd9 100644 --- a/packages/cli/src/commands/doctor.ts +++ b/packages/cli/src/commands/doctor.ts @@ -343,7 +343,9 @@ async function sendTestNotifications( const targets = new Map(); for (const [name, notifierConfig] of configuredNotifiers) { - targets.set(notifierConfig.plugin, { label: name, pluginName: notifierConfig.plugin }); + if (notifierConfig.plugin) { + targets.set(notifierConfig.plugin, { label: name, pluginName: notifierConfig.plugin }); + } } for (const name of activeNotifierNames) { diff --git a/packages/cli/src/commands/status.ts b/packages/cli/src/commands/status.ts index 625e06370..0e17d10a1 100644 --- a/packages/cli/src/commands/status.ts +++ b/packages/cli/src/commands/status.ts @@ -386,7 +386,7 @@ export function registerStatus(program: Command): void { let unverifiedTotal = 0; for (const projectId of projectIds) { const project: ProjectConfig | undefined = config.projects[projectId]; - if (!project?.tracker) continue; + if (!project?.tracker?.plugin) continue; const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker?.listIssues) continue; try { diff --git a/packages/cli/src/commands/verify.ts b/packages/cli/src/commands/verify.ts index 40d6ee043..c82223a87 100644 --- a/packages/cli/src/commands/verify.ts +++ b/packages/cli/src/commands/verify.ts @@ -60,6 +60,10 @@ async function getTracker( const registry = createPluginRegistry(); await registry.loadFromConfig(config, importPluginModuleFromSource); + if (!project.tracker.plugin) { + console.error(chalk.red("Project tracker plugin not configured.")); + process.exit(1); + } const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker) { console.error(chalk.red(`Tracker plugin "${project.tracker.plugin}" not found.`)); diff --git a/packages/web/src/app/api/issues/route.ts b/packages/web/src/app/api/issues/route.ts index 892d3f6c8..3212afd34 100644 --- a/packages/web/src/app/api/issues/route.ts +++ b/packages/web/src/app/api/issues/route.ts @@ -21,7 +21,7 @@ export async function GET(request: NextRequest) { for (const [projectId, project] of Object.entries(config.projects)) { if (projectFilter && projectId !== projectFilter) continue; - if (!project.tracker) continue; + if (!project.tracker?.plugin) continue; const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker?.listIssues) continue; @@ -76,7 +76,7 @@ export async function POST(request: NextRequest) { } const project = config.projects[projectId]; - if (!project.tracker) { + if (!project.tracker?.plugin) { return NextResponse.json({ error: "No tracker configured for this project" }, { status: 422 }); } diff --git a/packages/web/src/app/api/verify/route.ts b/packages/web/src/app/api/verify/route.ts index f0c00e847..44c0ef627 100644 --- a/packages/web/src/app/api/verify/route.ts +++ b/packages/web/src/app/api/verify/route.ts @@ -64,7 +64,7 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: projectErr }, { status: 404 }); } const project = config.projects[projectId]; - if (!project.tracker) { + if (!project.tracker?.plugin) { return NextResponse.json({ error: `Project ${projectId} has no tracker` }, { status: 422 }); } diff --git a/packages/web/src/lib/scm-webhooks.ts b/packages/web/src/lib/scm-webhooks.ts index 105dca848..06534e4e9 100644 --- a/packages/web/src/lib/scm-webhooks.ts +++ b/packages/web/src/lib/scm-webhooks.ts @@ -34,7 +34,7 @@ export function findWebhookProjects( pathname: string, ): WebhookProjectMatch[] { return Object.entries(config.projects).flatMap(([projectId, project]) => { - if (!project.scm) return []; + if (!project.scm?.plugin) return []; const webhookPath = getProjectWebhookPath(project); if (!webhookPath || webhookPath !== pathname) return []; const scm = registry.get("scm", project.scm.plugin); diff --git a/packages/web/src/lib/serialize.ts b/packages/web/src/lib/serialize.ts index d8668cb49..6cce47e10 100644 --- a/packages/web/src/lib/serialize.ts +++ b/packages/web/src/lib/serialize.ts @@ -364,7 +364,7 @@ export async function enrichSessionsMetadata( // Enrich issue labels (synchronous — must run before async title enrichment) projects.forEach((project, i) => { - if (!dashboardSessions[i].issueUrl || !project?.tracker) return; + if (!dashboardSessions[i].issueUrl || !project?.tracker?.plugin) return; const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker) return; enrichSessionIssue(dashboardSessions[i], tracker, project); @@ -385,7 +385,7 @@ export async function enrichSessionsMetadata( if (!dashboardSessions[i].issueUrl || !dashboardSessions[i].issueLabel) { return Promise.resolve(); } - if (!project?.tracker) return Promise.resolve(); + if (!project?.tracker?.plugin) return Promise.resolve(); const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker) return Promise.resolve(); return enrichSessionIssueTitle(dashboardSessions[i], tracker, project); diff --git a/packages/web/src/lib/services.ts b/packages/web/src/lib/services.ts index e815bf69f..e6a274b92 100644 --- a/packages/web/src/lib/services.ts +++ b/packages/web/src/lib/services.ts @@ -137,7 +137,7 @@ async function labelIssuesForVerification( for (const session of mergedSessions) { const key = `${session.projectId}:${session.issueId}`; const project = config.projects[session.projectId]; - if (!project?.tracker) { + if (!project?.tracker?.plugin) { processedIssues.add(key); continue; } @@ -180,7 +180,7 @@ async function relabelReopenedIssues( registry: PluginRegistry, ): Promise { for (const [, project] of Object.entries(config.projects)) { - if (!project.tracker) continue; + if (!project.tracker?.plugin) continue; const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker?.listIssues || !tracker.updateIssue) continue; @@ -240,7 +240,7 @@ export async function pollBacklog(): Promise { for (const [projectId, project] of Object.entries(config.projects)) { if (availableSlots <= 0) break; - if (!project.tracker) continue; + if (!project.tracker?.plugin) continue; const tracker = registry.get("tracker", project.tracker.plugin); if (!tracker?.listIssues) continue; @@ -352,7 +352,7 @@ export async function getBacklogIssues(): Promise("tracker", project.tracker.plugin); if (!tracker?.listIssues) continue; @@ -380,7 +380,7 @@ export async function getVerifyIssues(): Promise("tracker", project.tracker.plugin); if (!tracker?.listIssues) continue; @@ -404,6 +404,6 @@ export async function getVerifyIssues(): Promise("scm", project.scm.plugin); }