chat: rename claude_sessions → general_chat_sessions; drop dead chat_sessions
The default /chat working directory is used by both the Claude and OpenCode harnesses now, so its Claude-specific name was misleading. - Rename the dir + accessors: getClaudeSessionsCwd → getGeneralChatSessionsCwd, ensureClaudeSessionsCwd → ensureGeneralChatSessionsCwd, path segment claude_sessions → general_chat_sessions (data-path on disk + code + UI labels/comments). No history migration — the old Claude transcript slug is orphaned (intentionally). - Remove the vestigial chat_sessions dir (leftover from the retired session store): it only ever held empty claude/archived/ dirs, recreated by a signin hook. Drop that hook (+ its dead imports) and the 4 unused data-path accessors (getUserSessionsDir, getClaudeDir, getSessionDir, getArchivedSessionDir), and delete the dir. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -69,12 +69,18 @@ function NewChat({ resumeSummary, resumeSessionId, initialMessages }: NewChatPro
|
||||
invalidateClaudeSessions();
|
||||
}, [invalidateClaudeSessions]);
|
||||
|
||||
// context 'chat' tells the backend to run this session from the dedicated claude_sessions cwd, so
|
||||
// context 'chat' tells the backend to run this session from the dedicated general_chat_sessions cwd, so
|
||||
// its transcript lands in Claude's own store as an isolated project group (source of truth).
|
||||
const chat = useChat(undefined, locationState?.model, { resumeSummary, resumeSessionId, initialMessages, onTurnComplete, context: 'chat' });
|
||||
const chat = useChat(undefined, locationState?.model, {
|
||||
resumeSummary,
|
||||
resumeSessionId,
|
||||
initialMessages,
|
||||
onTurnComplete,
|
||||
context: 'chat',
|
||||
});
|
||||
|
||||
const sandboxed = !isSuperAdmin;
|
||||
// Run the session in the pwd chosen in the Sessions panel; null → backend default (claude_sessions).
|
||||
// Run the session in the pwd chosen in the Sessions panel; null → backend default (general_chat_sessions).
|
||||
const [activeCwd] = usePanelChannel<string | null>('chat:active-cwd', null);
|
||||
const cwd = activeCwd ? { path: activeCwd } : locationState?.cwd;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useChatPwds } from 'state/useClaudeSessions';
|
||||
import { DirPickerModal } from './DirPickerModal';
|
||||
|
||||
type PwdSelectorProps = {
|
||||
value: string | null; // null = the default claude_sessions dir
|
||||
value: string | null; // null = the default general_chat_sessions dir
|
||||
onChange: (cwd: string | null) => void;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ export const PwdSelector = ({ value, onChange }: PwdSelectorProps) => {
|
||||
const [custom, setCustom] = useState('');
|
||||
|
||||
const isDefaultActive = value === null || value === defaultCwd;
|
||||
const label = isDefaultActive ? 'claude_sessions' : basename(value!);
|
||||
const label = isDefaultActive ? 'general_chat_sessions' : basename(value!);
|
||||
|
||||
const pick = (cwd: string | null) => {
|
||||
onChange(cwd);
|
||||
@@ -35,7 +35,7 @@ export const PwdSelector = ({ value, onChange }: PwdSelectorProps) => {
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
title={isDefaultActive ? defaultCwd ?? 'Default' : value!}
|
||||
title={isDefaultActive ? (defaultCwd ?? 'Default') : value!}
|
||||
className="flex items-center gap-1.5 max-w-[13rem] rounded-md border border-duck-dark/10 dark:border-foreground/10 px-2 py-1 text-xs text-duck-dark/70 dark:text-foreground/70 hover:bg-duck-dark/5 dark:hover:bg-foreground/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<FolderOpen className="h-3.5 w-3.5 shrink-0 text-duck-teal/70" />
|
||||
@@ -58,7 +58,9 @@ export const PwdSelector = ({ value, onChange }: PwdSelectorProps) => {
|
||||
className={`flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs cursor-pointer hover:bg-duck-dark/5 dark:hover:bg-foreground/5 ${selected ? 'text-duck-teal' : 'text-duck-dark/70 dark:text-foreground/70'}`}
|
||||
>
|
||||
<Check className={`h-3.5 w-3.5 shrink-0 ${selected ? 'opacity-100' : 'opacity-0'}`} />
|
||||
<span className="min-w-0 flex-1 truncate">{p.isDefault ? 'Default · claude_sessions' : shorten(p.cwd)}</span>
|
||||
<span className="min-w-0 flex-1 truncate">
|
||||
{p.isDefault ? 'Default · general_chat_sessions' : shorten(p.cwd)}
|
||||
</span>
|
||||
{p.sessionCount > 0 && <span className="shrink-0 opacity-40">{p.sessionCount}</span>}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { PwdSelector } from './PwdSelector';
|
||||
// Clicking a session loads its transcript and continues the real Claude session via --resume.
|
||||
export const SessionList = () => {
|
||||
const navigate = useNavigate();
|
||||
// The working directory the list operates on (null = the default claude_sessions dir).
|
||||
// The working directory the list operates on (null = the default general_chat_sessions dir).
|
||||
const [activeCwd, setActiveCwd] = usePanelChannel<string | null>('chat:active-cwd', null);
|
||||
const { sessions, isLoading, refetch, loadSession, deleteSession, renameSession } = useClaudeSessions(activeCwd);
|
||||
const [selected, setSelected] = usePanelChannel<SelectedSession>('chat:selected-session', null);
|
||||
@@ -125,10 +125,20 @@ export const SessionList = () => {
|
||||
onBlur={commitRename}
|
||||
className="flex-1 min-w-0 bg-transparent border-b border-duck-teal/40 text-sm outline-none"
|
||||
/>
|
||||
<button onMouseDown={(ev) => ev.preventDefault()} onClick={commitRename} className="p-1 text-duck-teal hover:opacity-80 cursor-pointer" title="Save">
|
||||
<button
|
||||
onMouseDown={(ev) => ev.preventDefault()}
|
||||
onClick={commitRename}
|
||||
className="p-1 text-duck-teal hover:opacity-80 cursor-pointer"
|
||||
title="Save"
|
||||
>
|
||||
<Check className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<button onMouseDown={(ev) => ev.preventDefault()} onClick={() => setEditingId(null)} className="p-1 opacity-50 hover:opacity-100 cursor-pointer" title="Cancel">
|
||||
<button
|
||||
onMouseDown={(ev) => ev.preventDefault()}
|
||||
onClick={() => setEditingId(null)}
|
||||
className="p-1 opacity-50 hover:opacity-100 cursor-pointer"
|
||||
title="Cancel"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -145,7 +155,9 @@ export const SessionList = () => {
|
||||
<MessageSquare className="h-4 w-4 shrink-0 text-duck-teal/60" />
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-sm font-medium text-duck-dark/80 dark:text-foreground/80 truncate">{session.title}</div>
|
||||
<div className="text-sm font-medium text-duck-dark/80 dark:text-foreground/80 truncate">
|
||||
{session.title}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-duck-dark/40 dark:text-foreground/40">
|
||||
<span>
|
||||
{new Date(session.updatedAt).toLocaleDateString(undefined, {
|
||||
@@ -157,7 +169,9 @@ export const SessionList = () => {
|
||||
</span>
|
||||
<span>·</span>
|
||||
{session.harness === 'opencode' ? (
|
||||
<span className="rounded bg-duck-teal/10 px-1.5 py-0.5 font-medium text-duck-teal">OpenCode</span>
|
||||
<span className="rounded bg-duck-teal/10 px-1.5 py-0.5 font-medium text-duck-teal">
|
||||
OpenCode
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
{session.messageCount} msg{session.messageCount === 1 ? '' : 's'}
|
||||
@@ -170,10 +184,18 @@ export const SessionList = () => {
|
||||
{isConfirming ? (
|
||||
<div className="flex shrink-0 items-center gap-1 mr-2">
|
||||
<span className="text-xs text-red-500">Delete?</span>
|
||||
<button onClick={() => handleDelete(session.id)} className="p-1 rounded text-red-500 hover:bg-red-500/10 cursor-pointer" title="Confirm delete">
|
||||
<button
|
||||
onClick={() => handleDelete(session.id)}
|
||||
className="p-1 rounded text-red-500 hover:bg-red-500/10 cursor-pointer"
|
||||
title="Confirm delete"
|
||||
>
|
||||
<Check className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<button onClick={() => setConfirmingId(null)} className="p-1 rounded opacity-50 hover:opacity-100 cursor-pointer" title="Cancel">
|
||||
<button
|
||||
onClick={() => setConfirmingId(null)}
|
||||
className="p-1 rounded opacity-50 hover:opacity-100 cursor-pointer"
|
||||
title="Cancel"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -19,13 +19,20 @@ export type ClaudeSessionSummary = {
|
||||
export type ClaudeSessionMessage =
|
||||
| { role: 'user'; text: string }
|
||||
| { role: 'assistant'; id: string; text: string }
|
||||
| { role: 'tool'; toolName: string; toolInput: Record<string, unknown>; toolCallId: string; output?: string; isError?: boolean };
|
||||
| {
|
||||
role: 'tool';
|
||||
toolName: string;
|
||||
toolInput: Record<string, unknown>;
|
||||
toolCallId: string;
|
||||
output?: string;
|
||||
isError?: boolean;
|
||||
};
|
||||
|
||||
export type ClaudeSessionDetail = { id: string; model: string; cwd: string; messages: ClaudeSessionMessage[] };
|
||||
|
||||
export type ClaudePwd = { cwd: string; sessionCount: number; updatedAt: string; isDefault: boolean };
|
||||
|
||||
// Query string for the selected working directory (null/undefined = the default claude_sessions dir).
|
||||
// Query string for the selected working directory (null/undefined = the default general_chat_sessions dir).
|
||||
const cwdQuery = (cwd?: string | null) => (cwd ? `?cwd=${encodeURIComponent(cwd)}` : '');
|
||||
|
||||
/** The default /chat dir plus every directory that already has Claude sessions. */
|
||||
|
||||
Reference in New Issue
Block a user