diff --git a/apps/pi-extension/server/serverAnnotate.ts b/apps/pi-extension/server/serverAnnotate.ts index d632d38e8..8d5c7e03f 100644 --- a/apps/pi-extension/server/serverAnnotate.ts +++ b/apps/pi-extension/server/serverAnnotate.ts @@ -462,11 +462,12 @@ export async function startAnnotateServer(options: { handleShareHtml(res, url); } else if (url.pathname === "/api/config" && req.method === "POST") { try { - const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean }; + const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; platformReviewAttribution?: boolean }; const toSave: Record = {}; if (body.displayName !== undefined) toSave.displayName = body.displayName; if (body.diffOptions !== undefined) toSave.diffOptions = body.diffOptions; if (body.conventionalComments !== undefined) toSave.conventionalComments = body.conventionalComments; + if (body.platformReviewAttribution !== undefined) toSave.platformReviewAttribution = body.platformReviewAttribution; if (Object.keys(toSave).length > 0) saveConfig(toSave as Parameters[0]); json(res, { ok: true }); } catch { diff --git a/apps/pi-extension/server/serverPlan.ts b/apps/pi-extension/server/serverPlan.ts index d1b4c985f..1ac60b654 100644 --- a/apps/pi-extension/server/serverPlan.ts +++ b/apps/pi-extension/server/serverPlan.ts @@ -251,11 +251,12 @@ export async function startPlanReviewServer(options: { }); } else if (url.pathname === "/api/config" && req.method === "POST") { try { - const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; pfmReminder?: boolean }; + const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; platformReviewAttribution?: boolean; pfmReminder?: boolean }; const toSave: Record = {}; if (body.displayName !== undefined) toSave.displayName = body.displayName; if (body.diffOptions !== undefined) toSave.diffOptions = body.diffOptions; if (body.conventionalComments !== undefined) toSave.conventionalComments = body.conventionalComments; + if (body.platformReviewAttribution !== undefined) toSave.platformReviewAttribution = body.platformReviewAttribution; if (body.pfmReminder !== undefined) toSave.pfmReminder = body.pfmReminder; if (Object.keys(toSave).length > 0) saveConfig(toSave as Parameters[0]); json(res, { ok: true }); diff --git a/apps/pi-extension/server/serverReview.ts b/apps/pi-extension/server/serverReview.ts index 322b03839..1955e3a0a 100644 --- a/apps/pi-extension/server/serverReview.ts +++ b/apps/pi-extension/server/serverReview.ts @@ -2153,11 +2153,12 @@ export async function startReviewServer(options: { } } else if (url.pathname === "/api/config" && req.method === "POST") { try { - const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean }; + const body = (await parseBody(req)) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; platformReviewAttribution?: boolean }; const toSave: Record = {}; if (body.displayName !== undefined) toSave.displayName = body.displayName; if (body.diffOptions !== undefined) toSave.diffOptions = body.diffOptions; if (body.conventionalComments !== undefined) toSave.conventionalComments = body.conventionalComments; + if (body.platformReviewAttribution !== undefined) toSave.platformReviewAttribution = body.platformReviewAttribution; if (Object.keys(toSave).length > 0) saveConfig(toSave as Parameters[0]); json(res, { ok: true }); } catch { diff --git a/packages/review-editor/App.tsx b/packages/review-editor/App.tsx index 974b5aa1f..965c221dd 100644 --- a/packages/review-editor/App.tsx +++ b/packages/review-editor/App.tsx @@ -207,6 +207,7 @@ const ReviewApp: React.FC = () => { const diffFontFamily = useConfigValue('diffFontFamily'); const diffFontSize = useConfigValue('diffFontSize'); const diffTabSize = useConfigValue('diffTabSize'); + const platformReviewAttribution = useConfigValue('platformReviewAttribution'); // Global plan-look preference; surfaced here only by the shared 0.20.0 // look-and-feel announcement (the grid/clean chooser applies to plan review). const gridEnabled = useConfigValue('gridEnabled'); @@ -2443,7 +2444,7 @@ const ReviewApp: React.FC = () => { const bodyForTarget = (target: SubmissionTarget) => { const parts: string[] = []; if (generalComment) parts.push(generalComment); - parts.push('Review from Plannotator'); + if (platformReviewAttribution) parts.push('Review from Plannotator'); if (target.fileScopedBody) parts.push(target.fileScopedBody); return parts.join('\n\n'); }; @@ -2529,7 +2530,7 @@ const ReviewApp: React.FC = () => { } finally { setIsPlatformActioning(false); } - }, [platformOpenPR, platformLabel, mrLabel, prMetadata]); + }, [platformOpenPR, platformLabel, mrLabel, prMetadata, platformReviewAttribution]); const openPlatformDialog = useCallback((action: 'approve' | 'comment') => { const diffPaths = new Set(files.map(f => f.path)); diff --git a/packages/server/annotate.ts b/packages/server/annotate.ts index 7948ba8d7..3ed8e6984 100644 --- a/packages/server/annotate.ts +++ b/packages/server/annotate.ts @@ -472,11 +472,12 @@ export async function startAnnotateServer( // API: Update user config (write-back to ~/.plannotator/config.json) if (url.pathname === "/api/config" && req.method === "POST") { try { - const body = (await req.json()) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; conventionalLabels?: unknown[] | null }; + const body = (await req.json()) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; platformReviewAttribution?: boolean; conventionalLabels?: unknown[] | null }; const toSave: Record = {}; if (body.displayName !== undefined) toSave.displayName = body.displayName; if (body.diffOptions !== undefined) toSave.diffOptions = body.diffOptions; if (body.conventionalComments !== undefined) toSave.conventionalComments = body.conventionalComments; + if (body.platformReviewAttribution !== undefined) toSave.platformReviewAttribution = body.platformReviewAttribution; if (body.conventionalLabels !== undefined) toSave.conventionalLabels = body.conventionalLabels; if (Object.keys(toSave).length > 0) saveConfig(toSave as Parameters[0]); return Response.json({ ok: true }); diff --git a/packages/server/goal-setup.ts b/packages/server/goal-setup.ts index 9854635a5..75e238707 100644 --- a/packages/server/goal-setup.ts +++ b/packages/server/goal-setup.ts @@ -140,6 +140,7 @@ export async function startGoalSetupServer( displayName?: string; diffOptions?: Record; conventionalComments?: boolean; + platformReviewAttribution?: boolean; conventionalLabels?: unknown[] | null; }; const toSave: Record = {}; @@ -152,6 +153,9 @@ export async function startGoalSetupServer( if (body.conventionalComments !== undefined) { toSave.conventionalComments = body.conventionalComments; } + if (body.platformReviewAttribution !== undefined) { + toSave.platformReviewAttribution = body.platformReviewAttribution; + } if (body.conventionalLabels !== undefined) { toSave.conventionalLabels = body.conventionalLabels; } diff --git a/packages/server/index.ts b/packages/server/index.ts index 5519eaa6d..56277c126 100644 --- a/packages/server/index.ts +++ b/packages/server/index.ts @@ -328,11 +328,12 @@ export async function startPlannotatorServer( // API: Update user config (write-back to ~/.plannotator/config.json) if (url.pathname === "/api/config" && req.method === "POST") { try { - const body = (await req.json()) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; conventionalLabels?: unknown[] | null; pfmReminder?: boolean }; + const body = (await req.json()) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; platformReviewAttribution?: boolean; conventionalLabels?: unknown[] | null; pfmReminder?: boolean }; const toSave: Record = {}; if (body.displayName !== undefined) toSave.displayName = body.displayName; if (body.diffOptions !== undefined) toSave.diffOptions = body.diffOptions; if (body.conventionalComments !== undefined) toSave.conventionalComments = body.conventionalComments; + if (body.platformReviewAttribution !== undefined) toSave.platformReviewAttribution = body.platformReviewAttribution; if (body.conventionalLabels !== undefined) toSave.conventionalLabels = body.conventionalLabels; if (body.pfmReminder !== undefined) toSave.pfmReminder = body.pfmReminder; if (Object.keys(toSave).length > 0) saveConfig(toSave as Parameters[0]); diff --git a/packages/server/review.ts b/packages/server/review.ts index f84d6bdc6..b2f3ce3ff 100644 --- a/packages/server/review.ts +++ b/packages/server/review.ts @@ -2216,11 +2216,12 @@ export async function startReviewServer( // API: Update user config (write-back to ~/.plannotator/config.json) if (url.pathname === "/api/config" && req.method === "POST") { try { - const body = (await req.json()) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; conventionalLabels?: unknown[] | null }; + const body = (await req.json()) as { displayName?: string; diffOptions?: Record; conventionalComments?: boolean; platformReviewAttribution?: boolean; conventionalLabels?: unknown[] | null }; const toSave: Record = {}; if (body.displayName !== undefined) toSave.displayName = body.displayName; if (body.diffOptions !== undefined) toSave.diffOptions = body.diffOptions; if (body.conventionalComments !== undefined) toSave.conventionalComments = body.conventionalComments; + if (body.platformReviewAttribution !== undefined) toSave.platformReviewAttribution = body.platformReviewAttribution; if (body.conventionalLabels !== undefined) toSave.conventionalLabels = body.conventionalLabels; if (Object.keys(toSave).length > 0) saveConfig(toSave as Parameters[0]); return Response.json({ ok: true }); diff --git a/packages/shared/config.ts b/packages/shared/config.ts index 37d533edb..e4efefd15 100644 --- a/packages/shared/config.ts +++ b/packages/shared/config.ts @@ -88,6 +88,7 @@ export interface PlannotatorConfig { diffOptions?: DiffOptions; prompts?: PromptConfig; conventionalComments?: boolean; + platformReviewAttribution?: boolean; /** null = explicitly cleared (use defaults), undefined = not set */ conventionalLabels?: CCLabelConfig[] | null; /** @@ -201,6 +202,7 @@ export function getServerConfig(gitUser: string | null): { diffOptions?: DiffOptions; gitUser?: string; conventionalComments?: boolean; + platformReviewAttribution?: boolean; conventionalLabels?: CCLabelConfig[] | null; } { const cfg = loadConfig(); @@ -209,6 +211,7 @@ export function getServerConfig(gitUser: string | null): { diffOptions: cfg.diffOptions, gitUser: gitUser ?? undefined, ...(cfg.conventionalComments !== undefined && { conventionalComments: cfg.conventionalComments }), + ...(cfg.platformReviewAttribution !== undefined && { platformReviewAttribution: cfg.platformReviewAttribution }), ...(cfg.conventionalLabels !== undefined && { conventionalLabels: cfg.conventionalLabels }), }; } diff --git a/packages/ui/components/Settings.tsx b/packages/ui/components/Settings.tsx index e895acefa..b58f17cd8 100644 --- a/packages/ui/components/Settings.tsx +++ b/packages/ui/components/Settings.tsx @@ -487,6 +487,7 @@ function parseCCLabels(json: string | null): CCLabelConfig[] { const CommentsTab: React.FC = () => { const conventionalComments = useConfigValue('conventionalComments'); + const platformReviewAttribution = useConfigValue('platformReviewAttribution'); const labelsJson = useConfigValue('conventionalLabels'); const [labels, setLabels] = useState(() => parseCCLabels(labelsJson)); @@ -522,6 +523,15 @@ const CommentsTab: React.FC = () => { return ( <> + configStore.set('platformReviewAttribution', v)} + label="Plannotator Attribution" + description="Include “Review from Plannotator” in GitHub and GitLab review bodies" + /> + +
+ configStore.set('conventionalComments', v)} diff --git a/packages/ui/config/settings.ts b/packages/ui/config/settings.ts index 7615962d7..5c5807333 100644 --- a/packages/ui/config/settings.ts +++ b/packages/ui/config/settings.ts @@ -276,6 +276,20 @@ export const SETTINGS = { }, toServer: (v: boolean) => ({ conventionalComments: v }), }, + platformReviewAttribution: { + defaultValue: true as boolean, + fromCookie: () => { + const v = storage.getItem('plannotator-platform-review-attribution'); + return v === 'true' ? true : v === 'false' ? false : undefined; + }, + toCookie: (v: boolean) => storage.setItem('plannotator-platform-review-attribution', String(v)), + serverKey: 'platformReviewAttribution', + fromServer: (sc: Record) => { + const v = sc.platformReviewAttribution; + return typeof v === 'boolean' ? v : undefined; + }, + toServer: (v: boolean) => ({ platformReviewAttribution: v }), + }, /** JSON-serialized array of label configs, or null for defaults. * Synced to ~/.plannotator/config.json as a parsed array (not a string). */ conventionalLabels: {