feat: show session name in statusline (#155)

* feat: show session name in statusline

Reads the session slug (auto-generated) and custom title (set via
/rename) from the transcript JSONL and displays it in dim text after
the project/git info on both expanded and compact layouts.

Custom title takes priority over auto-generated slug when both exist.

* test: add session name coverage and harden integration spawn

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
This commit is contained in:
myaiexp
2026-03-03 04:54:01 +02:00
committed by GitHub
parent 883b281df4
commit bdfa4454b3
7 changed files with 92 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
import type { RenderContext } from '../../types.js';
import { getModelName, getProviderLabel } from '../../stdin.js';
import { cyan, magenta, yellow, red } from '../colors.js';
import { cyan, dim, magenta, yellow, red } from '../colors.js';
export function renderProjectLine(ctx: RenderContext): string | null {
const display = ctx.config?.display;
@@ -60,6 +60,10 @@ export function renderProjectLine(ctx: RenderContext): string | null {
parts.push(`${yellow(projectPath)}${gitPart}`);
}
if (ctx.transcript.sessionName) {
parts.push(dim(ctx.transcript.sessionName));
}
if (parts.length === 0) {
return null;
}

View File

@@ -100,6 +100,11 @@ export function renderSessionLine(ctx: RenderContext): string {
parts.push(`${yellow(projectPath)}${gitPart}`);
}
// Session name (custom title from /rename, or auto-generated slug)
if (ctx.transcript.sessionName) {
parts.push(dim(ctx.transcript.sessionName));
}
// Config counts (respects environmentThreshold)
if (display?.showConfigCounts !== false) {
const totalCounts = ctx.claudeMdCount + ctx.rulesCount + ctx.mcpCount + ctx.hooksCount;

View File

@@ -4,6 +4,9 @@ import type { TranscriptData, ToolEntry, AgentEntry, TodoItem } from './types.js
interface TranscriptLine {
timestamp?: string;
type?: string;
slug?: string;
customTitle?: string;
message?: {
content?: ContentBlock[];
};
@@ -33,6 +36,8 @@ export async function parseTranscript(transcriptPath: string): Promise<Transcrip
const agentMap = new Map<string, AgentEntry>();
let latestTodos: TodoItem[] = [];
const taskIdToIndex = new Map<string, number>();
let latestSlug: string | undefined;
let customTitle: string | undefined;
try {
const fileStream = fs.createReadStream(transcriptPath);
@@ -46,6 +51,11 @@ export async function parseTranscript(transcriptPath: string): Promise<Transcrip
try {
const entry = JSON.parse(line) as TranscriptLine;
if (entry.type === 'custom-title' && typeof entry.customTitle === 'string') {
customTitle = entry.customTitle;
} else if (typeof entry.slug === 'string') {
latestSlug = entry.slug;
}
processEntry(entry, toolMap, agentMap, taskIdToIndex, latestTodos, result);
} catch {
// Skip malformed lines
@@ -58,6 +68,7 @@ export async function parseTranscript(transcriptPath: string): Promise<Transcrip
result.tools = Array.from(toolMap.values()).slice(-20);
result.agents = Array.from(agentMap.values()).slice(-10);
result.todos = latestTodos;
result.sessionName = customTitle ?? latestSlug;
return result;
}

View File

@@ -72,6 +72,7 @@ export interface TranscriptData {
agents: AgentEntry[];
todos: TodoItem[];
sessionStart?: Date;
sessionName?: string;
}
export interface RenderContext {