fix: Fixed status line and updated welcome screen (#156)

This commit is contained in:
Devansh Jain
2025-12-05 12:36:26 -08:00
committed by GitHub
parent fde6fc0ab1
commit 78fdb23d00
2 changed files with 145 additions and 192 deletions

View File

@@ -1,6 +1,7 @@
import type { Letta } from "@letta-ai/letta-client";
import { Box, Text } from "ink";
import Link from "ink-link";
import type { AgentProvenance } from "../../agent/create";
import { getVersion } from "../../version";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { asciiLogo } from "./AsciiArt";
@@ -15,15 +16,92 @@ type LoadingState =
| "checking"
| "ready";
/**
* Generate status hints based on session type and block provenance.
* Pure function - no React dependencies.
*/
export function getAgentStatusHints(
continueSession: boolean,
agentState?: Letta.AgentState | null,
agentProvenance?: AgentProvenance | null,
): string[] {
const hints: string[] = [];
// For resumed agents, show memory blocks and --new hint
if (continueSession) {
if (agentState?.memory?.blocks) {
const blocks = agentState.memory.blocks;
const count = blocks.length;
const labels = blocks
.map((b) => b.label)
.filter(Boolean)
.join(", ");
if (labels) {
hints.push(
`→ Attached ${count} memory block${count !== 1 ? "s" : ""}: ${labels}`,
);
}
}
hints.push("→ To create a new agent, use --new");
return hints;
}
// For new agents with provenance, show block sources
if (agentProvenance) {
// Blocks reused from existing storage
const reusedGlobalBlocks = agentProvenance.blocks
.filter((b) => b.source === "global")
.map((b) => b.label);
const reusedProjectBlocks = agentProvenance.blocks
.filter((b) => b.source === "project")
.map((b) => b.label);
// New blocks - categorize by where they'll be stored
// (project/skills → .letta/, others → ~/.letta/)
const newBlocks = agentProvenance.blocks.filter((b) => b.source === "new");
const newGlobalBlocks = newBlocks
.filter((b) => b.label !== "project" && b.label !== "skills")
.map((b) => b.label);
const newProjectBlocks = newBlocks
.filter((b) => b.label === "project" || b.label === "skills")
.map((b) => b.label);
if (reusedGlobalBlocks.length > 0) {
hints.push(
`→ Reusing from global (~/.letta/): ${reusedGlobalBlocks.join(", ")}`,
);
}
if (newGlobalBlocks.length > 0) {
hints.push(
`→ Created in global (~/.letta/): ${newGlobalBlocks.join(", ")}`,
);
}
if (reusedProjectBlocks.length > 0) {
hints.push(
`→ Reusing from project (.letta/): ${reusedProjectBlocks.join(", ")}`,
);
}
if (newProjectBlocks.length > 0) {
hints.push(
`→ Created in project (.letta/): ${newProjectBlocks.join(", ")}`,
);
}
}
return hints;
}
export function WelcomeScreen({
loadingState,
continueSession,
agentState,
agentProvenance,
terminalWidth: frozenWidth,
}: {
loadingState: LoadingState;
continueSession?: boolean;
agentState?: Letta.AgentState | null;
agentProvenance?: AgentProvenance | null;
terminalWidth?: number;
}) {
const currentWidth = useTerminalWidth();
@@ -32,94 +110,20 @@ export function WelcomeScreen({
const version = getVersion();
const agentId = agentState?.id;
// Split logo into lines for side-by-side rendering
const logoLines = asciiLogo.trim().split("\n");
// Determine verbosity level based on terminal width
const isWide = terminalWidth >= 120;
const isMedium = terminalWidth >= 80;
const getMemoryBlocksText = () => {
if (!agentState?.memory?.blocks) {
return null;
}
const blocks = agentState.memory.blocks;
const count = blocks.length;
const labels = blocks
.map((b) => b.label)
.filter(Boolean)
.join(", ");
if (isWide && labels) {
return `attached ${count} memory block${count !== 1 ? "s" : ""} (${labels})`;
}
if (isMedium) {
return `attached ${count} memory block${count !== 1 ? "s" : ""}`;
}
return null;
};
const getAgentMessage = () => {
if (loadingState === "ready") {
const memoryText = getMemoryBlocksText();
const baseText =
continueSession && agentId
? "Resumed agent"
: agentId
? "Created a new agent"
: "Ready to go!";
if (memoryText) {
return `${baseText}, ${memoryText}`;
}
return baseText;
}
if (loadingState === "initializing") {
return continueSession ? "Resuming agent..." : "Creating agent...";
}
if (loadingState === "assembling") {
return "Assembling tools...";
}
if (loadingState === "upserting") {
return "Upserting tools...";
}
if (loadingState === "linking") {
return "Attaching Letta Code tools...";
}
if (loadingState === "unlinking") {
return "Removing Letta Code tools...";
}
if (loadingState === "checking") {
return "Checking for pending approvals...";
}
return "";
};
const getPathLine = () => {
if (isMedium) {
return `Running in ${cwd}`;
}
return cwd;
};
const getAgentLink = () => {
if (loadingState === "ready" && agentId) {
const url = `https://app.letta.com/agents/${agentId}`;
if (isWide) {
return { text: url, url };
}
if (isMedium) {
return { text: agentId, url };
}
return { text: "Click to view in ADE", url };
}
return null;
};
const agentMessage = getAgentMessage();
const pathLine = getPathLine();
const agentLink = getAgentLink();
const statusMessage = getStatusMessage(
loadingState,
!!continueSession,
agentId,
);
const pathLine = isMedium ? `Running in ${cwd}` : cwd;
const agentUrl = agentId ? `https://app.letta.com/agents/${agentId}` : null;
const hints =
loadingState === "ready"
? getAgentStatusHints(!!continueSession, agentState, agentProvenance)
: [];
return (
<Box flexDirection="row" marginTop={1}>
@@ -139,13 +143,55 @@ export function WelcomeScreen({
Letta Code v{version}
</Text>
<Text dimColor>{pathLine}</Text>
{agentMessage && <Text dimColor>{agentMessage}</Text>}
{agentLink && (
<Link url={agentLink.url}>
<Text dimColor>{agentLink.text}</Text>
</Link>
{statusMessage && (
<Text dimColor>
{statusMessage}
{loadingState === "ready" && agentUrl && (
<>
{": "}
<Link url={agentUrl}>
<Text dimColor>{agentUrl}</Text>
</Link>
</>
)}
</Text>
)}
{hints.map((hint, idx) => (
// biome-ignore lint/suspicious/noArrayIndexKey: Hint lines are static and never reorder
<Text key={idx} dimColor>
{hint}
</Text>
))}
</Box>
</Box>
);
}
function getStatusMessage(
loadingState: LoadingState,
continueSession: boolean,
agentId?: string,
): string {
switch (loadingState) {
case "ready":
return continueSession && agentId
? "Resumed agent"
: agentId
? "Created a new agent"
: "Ready to go!";
case "initializing":
return continueSession ? "Resuming agent..." : "Creating agent...";
case "assembling":
return "Assembling tools...";
case "upserting":
return "Upserting tools...";
case "linking":
return "Attaching Letta Code tools...";
case "unlinking":
return "Removing Letta Code tools...";
case "checking":
return "Checking for pending approvals...";
default:
return "";
}
}