From 5ed787a8caf974fe27c0db4ae7dceba98323f2f5 Mon Sep 17 00:00:00 2001 From: CRT Date: Thu, 15 Oct 2026 06:28:52 +0800 Subject: [PATCH] =?UTF-8?q?desktop=20AI=20fix:=20a=20claude=20CLI=20withou?= =?UTF-8?q?t=20a=20sign-in=20("Not=20logged=20in=20=C2=B7=20Please=20run?= =?UTF-8?q?=20/login",=20seen=20in=20a=20real=20headless=20run)=20shows=20?= =?UTF-8?q?'not=20signed=20in'=20with=20the=20copy-for-AI=20fallback=20ins?= =?UTF-8?q?tead=20of=20a=20bare=20failure;=20the=20flags=20were=20checked?= =?UTF-8?q?=20against=20a=20real=20claude=20run=20(it=20edited=20the=20fil?= =?UTF-8?q?e)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/desktop/src/i18n/en.ts | 1 + apps/desktop/src/i18n/tr.ts | 1 + apps/desktop/src/ui/AiFix.tsx | 8 +++++++- apps/desktop/tests/ai-signed-out.test.ts | 10 ++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/tests/ai-signed-out.test.ts diff --git a/apps/desktop/src/i18n/en.ts b/apps/desktop/src/i18n/en.ts index 126de0b..64283b9 100644 --- a/apps/desktop/src/i18n/en.ts +++ b/apps/desktop/src/i18n/en.ts @@ -453,6 +453,7 @@ export const en = { 'ai.dirty': 'Uncommitted files in the app folder ({count}): commit or stash them first, so Undo can restore exactly what the AI changes.', 'ai.noGit': 'The app folder is not in a git repository: Fix with AI needs git for the diff and Undo.', 'ai.cancelled': 'Cancelled', + 'ai.signedOut': 'Claude Code is not signed in: run claude once in a terminal and sign in with /login, or copy the prompt for your AI agent', 'ai.timedOut': 'Stopped after 10 minutes', 'ai.noChanges': 'No files changed', 'ai.changed': 'Changed files: {count}', diff --git a/apps/desktop/src/i18n/tr.ts b/apps/desktop/src/i18n/tr.ts index 9a27102..fd814f4 100644 --- a/apps/desktop/src/i18n/tr.ts +++ b/apps/desktop/src/i18n/tr.ts @@ -455,6 +455,7 @@ export const tr: Record = { 'ai.dirty': 'Uygulama klasöründe commit edilmemiş {count} dosya var: önce commit et ya da stash yap ki Geri al tam olarak yapay zekânın değiştirdiğini geri yüklesin.', 'ai.noGit': 'Uygulama klasörü bir git deposunda değil: Yapay zekâyla düzelt fark ve Geri al için git ister.', 'ai.cancelled': 'İptal edildi', + 'ai.signedOut': 'Claude Code oturum açmamış: bir terminalde claude çalıştırıp /login ile giriş yapın ya da istemi yapay zekâ ajanınız için kopyalayın', 'ai.timedOut': '10 dakika sonra durduruldu', 'ai.noChanges': 'Hiçbir dosya değişmedi', 'ai.changed': 'Değişen dosya: {count}', diff --git a/apps/desktop/src/ui/AiFix.tsx b/apps/desktop/src/ui/AiFix.tsx index d0c85d4..b864dcd 100644 --- a/apps/desktop/src/ui/AiFix.tsx +++ b/apps/desktop/src/ui/AiFix.tsx @@ -90,7 +90,9 @@ export function AiFix({ projectId, f, claude, busy, setBusy, rescan, stillThere, ); else if (p.kind === 'done') { const o = p.run.outcome; - const head = o.cancelled ? t('ai.cancelled') : o.timed_out ? t('ai.timedOut') : !p.run.changes.length ? t('ai.noChanges') : t('ai.changed', { count: p.run.changes.length }); + // a claude CLI that is not signed in answers at once ("Not logged in · Please run /login"): say so, offer the copy + const signedOut = !p.run.changes.length && signedOutText(o.summary); + const head = o.cancelled ? t('ai.cancelled') : o.timed_out ? t('ai.timedOut') : signedOut ? t('ai.signedOut') : !p.run.changes.length ? t('ai.noChanges') : t('ai.changed', { count: p.run.changes.length }); panel = (
{head}{p.run.changes.length > 0 && !p.undone ? ` · ${stillThere ? t('ai.stillThere') : t('ai.gone')}` : ''} @@ -100,6 +102,7 @@ export function AiFix({ projectId, f, claude, busy, setBusy, rescan, stillThere,
{p.run.changes.length > 0 && } {p.run.changes.length > 0 && } + {signedOut && } {!p.run.changes.length && }
)} @@ -113,3 +116,6 @@ export function AiFix({ projectId, f, claude, busy, setBusy, rescan, stillThere, ); return <>{p.kind === 'idle' || p.kind === 'running' ?
{button}
: null}{panel}; } + +/** The claude CLI's answer when it has no sign-in (it exits at once, nothing changed). */ +export const signedOutText = (s: string): boolean => /not logged in|please run \/login|invalid api key|authentication/i.test(s); diff --git a/apps/desktop/tests/ai-signed-out.test.ts b/apps/desktop/tests/ai-signed-out.test.ts new file mode 100644 index 0000000..af37a3a --- /dev/null +++ b/apps/desktop/tests/ai-signed-out.test.ts @@ -0,0 +1,10 @@ +// The claude CLI without a sign-in exits at once with this result text (seen with a real run): the AI fix panel says +// "not signed in" and offers the copy instead of a bare failure. +import { expect, test } from 'bun:test'; +import { signedOutText } from '../src/ui/AiFix'; + +test('signed-out claude answers are recognised', () => { + expect(signedOutText('Not logged in · Please run /login')).toBe(true); + expect(signedOutText('Invalid API key · Fix external API key')).toBe(true); + expect(signedOutText('Removed NSAllowsArbitraryLoads; no http URLs are loaded.')).toBe(false); +});