diff --git a/apps/web/src/components/chat/ProviderStatusBanner.tsx b/apps/web/src/components/chat/ProviderStatusBanner.tsx
index e709e75da3..73ba4ba405 100644
--- a/apps/web/src/components/chat/ProviderStatusBanner.tsx
+++ b/apps/web/src/components/chat/ProviderStatusBanner.tsx
@@ -2,6 +2,8 @@ import { PROVIDER_DISPLAY_NAMES, type ServerProvider } from "@t3tools/contracts"
import { memo } from "react";
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
import { CircleAlertIcon } from "lucide-react";
+import { PROVIDER_OPTIONS } from "~/session-logic";
+import { ensureSentenceEnds } from "~/lib/utils";
export const ProviderStatusBanner = memo(function ProviderStatusBanner({
status,
@@ -19,13 +21,28 @@ export const ProviderStatusBanner = memo(function ProviderStatusBanner({
: `${providerLabel} provider has limited availability.`;
const title = `${providerLabel} provider status`;
+ const opts = PROVIDER_OPTIONS.find((opt) => opt.value === status.provider);
+
return (
{title}
- {status.message ?? defaultMessage}
+ {ensureSentenceEnds(status.message ?? defaultMessage)}
+ {opts?.docsUrl ? (
+ <>
+ {" "}
+
+ Installation Guide
+
+ >
+ ) : null}
diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts
index b5834606b1..7a7c3fd037 100644
--- a/apps/web/src/lib/utils.ts
+++ b/apps/web/src/lib/utils.ts
@@ -34,3 +34,11 @@ export const newProjectId = (): ProjectId => ProjectId.makeUnsafe(randomUUID());
export const newThreadId = (): ThreadId => ThreadId.makeUnsafe(randomUUID());
export const newMessageId = (): MessageId => MessageId.makeUnsafe(randomUUID());
+
+export const ensureSentenceEnds = (value: string): string => {
+ const trimmed = value.trim();
+ if ([".", "!", "?"].includes(trimmed.at(-1) ?? "")) {
+ return trimmed;
+ }
+ return `${trimmed}.`;
+};
diff --git a/apps/web/src/session-logic.ts b/apps/web/src/session-logic.ts
index 83a95d6313..541c98252f 100644
--- a/apps/web/src/session-logic.ts
+++ b/apps/web/src/session-logic.ts
@@ -26,10 +26,26 @@ export const PROVIDER_OPTIONS: Array<{
value: ProviderPickerKind;
label: string;
available: boolean;
+ docsUrl: string;
}> = [
- { value: "codex", label: "Codex", available: true },
- { value: "claudeAgent", label: "Claude", available: true },
- { value: "cursor", label: "Cursor", available: false },
+ {
+ value: "codex",
+ label: "Codex",
+ available: true,
+ docsUrl: "https://developers.openai.com/codex/cli/#cli-setup",
+ },
+ {
+ value: "claudeAgent",
+ label: "Claude",
+ available: true,
+ docsUrl: "https://code.claude.com/docs/en/quickstart#step-1-install-claude-code",
+ },
+ {
+ value: "cursor",
+ label: "Cursor",
+ available: false,
+ docsUrl: "https://cursor.com/docs/cli/installation#installation",
+ },
];
export interface WorkLogEntry {