Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/codebuddy-hud/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const DEFAULT_CONFIG: HudConfig = {
tasks: false,
contextUsage: true,
contextValues: false,
sessionId: false,
},
colors: {
model: 'blue',
Expand Down
18 changes: 15 additions & 3 deletions plugins/codebuddy-hud/src/render/identity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { RenderContext } from '../types.js';
import { getModelName, getProjectPath, getVersion } from '../stdin.js';
import { colorize } from './colors.js';
import { getModelName, getProjectPath, getVersion, getSessionId } from '../stdin.js';
import { colorize, dim } from './colors.js';

export function renderIdentityLine(ctx: RenderContext): string | null {
const { config, stdin, gitStatus } = ctx;
const { config, stdin, gitStatus, transcript } = ctx;
const { display, colors } = config;

const parts: string[] = [];
Expand Down Expand Up @@ -32,6 +32,18 @@ export function renderIdentityLine(ctx: RenderContext): string | null {
}
}

if (display.sessionId) {
const sessionName = transcript?.sessionName;
if (sessionName) {
parts.push(colorize(sessionName, 'brightCyan'));
} else {
const sid = getSessionId(stdin);
if (sid) {
parts.push(dim(`#${sid}`));
}
}
}

if (parts.length === 0) return null;

return parts.join(' | ');
Expand Down
9 changes: 7 additions & 2 deletions plugins/codebuddy-hud/src/render/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { renderIdentityLine } from './identity.js';
import { renderStatsLine, renderContextBar } from './stats.js';
import { renderActivityLine } from './activity.js';
import { getTerminalWidth, wrapLineToWidth } from './width.js';
import { getModelName, getProjectPath } from '../stdin.js';
import { colorize } from './colors.js';
import { getModelName, getProjectPath, getSessionId } from '../stdin.js';
import { colorize, dim } from './colors.js';

const RESET = '\x1b[0m';

Expand Down Expand Up @@ -52,6 +52,11 @@ function renderCompact(ctx: RenderContext): string[] {
if (ver) parts.push(`v${ver}`);
}

if (display.sessionId) {
const sid = getSessionId(stdin);
if (sid) parts.push(dim(`#${sid}`));
}

// Stats (without context, since it's already shown above)
const stats = renderStatsLine(ctx, { includeContext: false });
if (stats) parts.push(stats);
Expand Down
6 changes: 6 additions & 0 deletions plugins/codebuddy-hud/src/stdin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ export function getContextWindow(stdin: Partial<StdinData>): ContextWindowInfo {
export function getVersion(stdin: Partial<StdinData>): string | null {
return stdin.version ?? null;
}

export function getSessionId(stdin: Partial<StdinData>, length = 8): string | null {
const id = stdin.session_id ?? null;
if (!id) return null;
return id.slice(0, length);
}
89 changes: 89 additions & 0 deletions plugins/codebuddy-hud/src/transcript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import * as os from 'node:os';
import type { TranscriptData, ToolEntry, AgentEntry, TaskItem } from './types.js';

interface TranscriptLine {
type?: string;
timestamp?: string;
aiTitle?: string;
customTitle?: string;
callId?: string;
name?: string;
arguments?: string;
isError?: boolean;
message?: {
content?: ContentBlock[];
};
Expand Down Expand Up @@ -44,6 +51,7 @@ interface CacheFile {
agents: SerializedAgentEntry[];
tasks: TaskItem[];
sessionStart?: string;
sessionName?: string;
};
}

Expand Down Expand Up @@ -88,6 +96,7 @@ function readCache(transcriptPath: string, state: FileState): TranscriptData | n
})),
tasks: cached.data.tasks,
sessionStart: cached.data.sessionStart ? new Date(cached.data.sessionStart) : undefined,
sessionName: cached.data.sessionName,
};
} catch {
return null;
Expand All @@ -113,6 +122,7 @@ function writeCache(transcriptPath: string, state: FileState, data: TranscriptDa
})),
tasks: data.tasks,
sessionStart: data.sessionStart?.toISOString(),
sessionName: data.sessionName,
},
};
fs.writeFileSync(getCachePath(transcriptPath), JSON.stringify(payload), 'utf8');
Expand Down Expand Up @@ -175,6 +185,7 @@ export async function parseTranscript(transcriptPath: string): Promise<Transcrip
const tasks: TaskItem[] = [];
const taskIdToIndex = new Map<string, number>();
let sessionStart: Date | undefined;
let sessionName: string | undefined;
let parsedCleanly = false;

try {
Expand All @@ -193,6 +204,83 @@ export async function parseTranscript(transcriptPath: string): Promise<Transcrip
sessionStart = timestamp;
}

// 提取 /rename 或 AI 自动生成的会话名称(取最新的)
if (entry.type === 'ai-title' && typeof entry.aiTitle === 'string' && entry.aiTitle) {
sessionName = entry.aiTitle;
}
if (entry.type === 'custom-title' && typeof entry.customTitle === 'string' && entry.customTitle) {
sessionName = entry.customTitle;
}

// CodeBuddy Code format: top-level function_call / function_call_result entries
if (entry.type === 'function_call' && entry.callId && entry.name) {
const input = entry.arguments
? (() => { try { return JSON.parse(entry.arguments) as Record<string, unknown>; } catch { return undefined; } })()
: undefined;
if (entry.name === 'Task') {
agentMap.set(entry.callId, {
id: entry.callId,
type: (input?.subagent_type as string) ?? 'unknown',
model: (input?.model as string) ?? undefined,
description: (input?.description as string) ?? undefined,
status: 'running',
startTime: timestamp,
});
} else if (entry.name === 'TaskCreate') {
const subject = typeof input?.subject === 'string' ? input.subject : '';
const description = typeof input?.description === 'string' ? input.description : '';
const taskContent = subject || description || 'Untitled task';
const status = normalizeTaskStatus(input?.status) ?? 'pending';
tasks.push({ content: taskContent, status });
const taskId = typeof input?.taskId === 'string' || typeof input?.taskId === 'number'
? String(input.taskId)
: entry.callId;
taskIdToIndex.set(taskId, tasks.length - 1);
} else if (entry.name === 'TaskUpdate') {
const taskId = typeof input?.taskId === 'string' || typeof input?.taskId === 'number'
? String(input.taskId)
: undefined;
if (taskId) {
let index = taskIdToIndex.get(taskId);
if (index === undefined && /^\d+$/.test(taskId)) {
const numIdx = parseInt(taskId, 10) - 1;
if (numIdx >= 0 && numIdx < tasks.length) index = numIdx;
}
if (index !== undefined) {
const newStatus = normalizeTaskStatus(input?.status);
if (newStatus) tasks[index].status = newStatus;
const newSubject = typeof input?.subject === 'string' ? input.subject : '';
const newDesc = typeof input?.description === 'string' ? input.description : '';
const newContent = newSubject || newDesc;
if (newContent) tasks[index].content = newContent;
}
}
} else {
toolMap.set(entry.callId, {
id: entry.callId,
name: entry.name,
target: extractTarget(entry.name, input),
status: 'running',
startTime: timestamp,
});
}
continue;
}

if (entry.type === 'function_call_result' && entry.callId) {
const tool = toolMap.get(entry.callId);
if (tool) {
tool.status = entry.isError ? 'error' : 'completed';
tool.endTime = timestamp;
}
const agent = agentMap.get(entry.callId);
if (agent) {
agent.status = 'completed';
agent.endTime = timestamp;
}
continue;
}

const content = entry.message?.content;
if (!content || !Array.isArray(content)) continue;

Expand Down Expand Up @@ -281,6 +369,7 @@ export async function parseTranscript(transcriptPath: string): Promise<Transcrip
agents: Array.from(agentMap.values()).slice(-10),
tasks,
sessionStart,
sessionName,
};

if (parsedCleanly) {
Expand Down
2 changes: 2 additions & 0 deletions plugins/codebuddy-hud/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface TranscriptData {
agents: AgentEntry[];
tasks: TaskItem[];
sessionStart?: Date;
sessionName?: string;
}

export interface GitStatus {
Expand All @@ -92,6 +93,7 @@ export interface HudConfig {
tasks: boolean;
contextUsage: boolean;
contextValues: boolean;
sessionId: boolean;
};
colors: {
model: string;
Expand Down