Skip to content

Commit 916b888

Browse files
7418claude
andcommitted
fix: require valid project directory for session creation
- Backend: POST /api/chat/sessions now validates working_directory exists (400 MISSING_DIRECTORY / INVALID_DIRECTORY) - db.ts: remove process.cwd() fallback in createSession - ChatListPanel: validate last-working-directory before use, open FolderPicker when invalid; add SSR guard for localStorage - NewChatPage: block sending without a project directory selected Bump version to 0.10.5. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ae8a905 commit 916b888

File tree

6 files changed

+63
-12
lines changed

6 files changed

+63
-12
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codepilot",
3-
"version": "0.10.4",
3+
"version": "0.10.5",
44
"private": true,
55
"author": {
66
"name": "op7418",

src/app/api/chat/sessions/route.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { NextRequest } from 'next/server';
2+
import fs from 'fs/promises';
23
import { getAllSessions, createSession } from '@/lib/db';
34
import type { CreateSessionRequest, SessionsResponse, SessionResponse } from '@/types';
45

@@ -17,6 +18,25 @@ export async function GET() {
1718
export async function POST(request: NextRequest) {
1819
try {
1920
const body: CreateSessionRequest = await request.json();
21+
22+
// Validate working_directory is provided
23+
if (!body.working_directory) {
24+
return Response.json(
25+
{ error: 'Working directory is required', code: 'MISSING_DIRECTORY' },
26+
{ status: 400 },
27+
);
28+
}
29+
30+
// Validate directory actually exists on disk
31+
try {
32+
await fs.access(body.working_directory);
33+
} catch {
34+
return Response.json(
35+
{ error: 'Directory does not exist', code: 'INVALID_DIRECTORY' },
36+
{ status: 400 },
37+
);
38+
}
39+
2040
const session = createSession(
2141
body.title,
2242
body.model,

src/app/chat/page.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ export default function NewChatPage() {
7878
async (content: string) => {
7979
if (isStreaming) return;
8080

81+
// Require a project directory before sending
82+
if (!workingDir.trim()) {
83+
const hint: Message = {
84+
id: 'hint-' + Date.now(),
85+
session_id: '',
86+
role: 'assistant',
87+
content: '**Please select a project directory first.** Use the folder picker in the toolbar below to choose a working directory before sending a message.',
88+
created_at: new Date().toISOString(),
89+
token_usage: null,
90+
};
91+
setMessages((prev) => [...prev, hint]);
92+
return;
93+
}
94+
8195
setIsStreaming(true);
8296
setStreamingContent('');
8397
setToolUses([]);
@@ -90,14 +104,12 @@ export default function NewChatPage() {
90104
let sessionId = '';
91105

92106
try {
93-
// Create a new session with optional working directory
107+
// Create a new session with working directory
94108
const createBody: Record<string, string> = {
95109
title: content.slice(0, 50),
96110
mode,
111+
working_directory: workingDir.trim(),
97112
};
98-
if (workingDir.trim()) {
99-
createBody.working_directory = workingDir.trim();
100-
}
101113

102114
const createRes = await fetch('/api/chat/sessions', {
103115
method: 'POST',

src/components/layout/ChatListPanel.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ function formatRelativeTime(dateStr: string): string {
5353
const COLLAPSED_PROJECTS_KEY = "codepilot:collapsed-projects";
5454

5555
function loadCollapsedProjects(): Set<string> {
56+
if (typeof window === 'undefined') return new Set();
5657
try {
5758
const raw = localStorage.getItem(COLLAPSED_PROJECTS_KEY);
5859
if (raw) return new Set(JSON.parse(raw));
@@ -129,27 +130,45 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
129130
const [creatingChat, setCreatingChat] = useState(false);
130131

131132
const handleNewChat = useCallback(async () => {
132-
const lastDir = localStorage.getItem("codepilot:last-working-directory");
133+
const lastDir = typeof window !== 'undefined'
134+
? localStorage.getItem("codepilot:last-working-directory")
135+
: null;
136+
133137
if (!lastDir) {
134-
router.push("/chat");
138+
// No saved directory — let user pick one
139+
setFolderPickerOpen(true);
135140
return;
136141
}
142+
143+
// Validate the saved directory still exists
137144
setCreatingChat(true);
138145
try {
146+
const checkRes = await fetch(
147+
`/api/files/browse?dir=${encodeURIComponent(lastDir)}`
148+
);
149+
if (!checkRes.ok) {
150+
// Directory is gone — clear stale value and prompt user
151+
localStorage.removeItem("codepilot:last-working-directory");
152+
setFolderPickerOpen(true);
153+
return;
154+
}
155+
139156
const res = await fetch("/api/chat/sessions", {
140157
method: "POST",
141158
headers: { "Content-Type": "application/json" },
142159
body: JSON.stringify({ working_directory: lastDir }),
143160
});
144161
if (!res.ok) {
145-
router.push("/chat");
162+
// Backend rejected it (e.g. INVALID_DIRECTORY) — prompt user
163+
localStorage.removeItem("codepilot:last-working-directory");
164+
setFolderPickerOpen(true);
146165
return;
147166
}
148167
const data = await res.json();
149168
router.push(`/chat/${data.session.id}`);
150169
window.dispatchEvent(new CustomEvent("session-created"));
151170
} catch {
152-
router.push("/chat");
171+
setFolderPickerOpen(true);
153172
} finally {
154173
setCreatingChat(false);
155174
}

src/lib/db.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export function createSession(
229229
const db = getDb();
230230
const id = crypto.randomBytes(16).toString('hex');
231231
const now = new Date().toISOString().replace('T', ' ').split('.')[0];
232-
const wd = workingDirectory || process.cwd();
232+
const wd = workingDirectory || '';
233233
const projectName = path.basename(wd);
234234

235235
db.prepare(

0 commit comments

Comments
 (0)