From 0325b93d6f3662a7d6868fbd4e3e9d2a22262168 Mon Sep 17 00:00:00 2001 From: ocracy Date: Sun, 18 Jan 2026 02:03:15 +0300 Subject: [PATCH 1/4] feat: add initial prompt support for projects - Add initial_prompt column to projects table - Add migration #12 for existing databases - Update ProjectSettingsDialog with Initial Prompt textarea - Combine project and session prompts on session creation (project prompt + "\n\n" + session prompt) Co-Authored-By: Claude Opus 4.5 --- app/api/projects/[id]/route.ts | 18 +++++++++++++++-- app/api/sessions/route.ts | 20 +++++++++++++++++-- components/Projects/ProjectSettingsDialog.tsx | 20 +++++++++++++++++++ data/projects/queries.ts | 3 +++ lib/db/migrations.ts | 7 +++++++ lib/db/queries.ts | 6 +++--- lib/db/schema.ts | 1 + lib/db/types.ts | 1 + lib/projects.ts | 14 ++++++++++++- 9 files changed, 82 insertions(+), 8 deletions(-) diff --git a/app/api/projects/[id]/route.ts b/app/api/projects/[id]/route.ts index 3848bde..8f0d63c 100644 --- a/app/api/projects/[id]/route.ts +++ b/app/api/projects/[id]/route.ts @@ -35,7 +35,14 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) { try { const { id } = await params; const body = await request.json(); - const { name, workingDirectory, agentType, defaultModel, expanded } = body; + const { + name, + workingDirectory, + agentType, + defaultModel, + initialPrompt, + expanded, + } = body; // Handle expanded toggle separately if (typeof expanded === "boolean") { @@ -43,12 +50,19 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) { } // Update other fields if provided - if (name || workingDirectory || agentType || defaultModel) { + if ( + name || + workingDirectory || + agentType || + defaultModel || + initialPrompt !== undefined + ) { const project = updateProject(id, { name, working_directory: workingDirectory, agent_type: agentType, default_model: defaultModel, + initial_prompt: initialPrompt, }); if (!project) { diff --git a/app/api/sessions/route.ts b/app/api/sessions/route.ts index 9bc4554..3b6fe02 100644 --- a/app/api/sessions/route.ts +++ b/app/api/sessions/route.ts @@ -6,6 +6,7 @@ import { createWorktree } from "@/lib/worktrees"; import { setupWorktree, type SetupResult } from "@/lib/env-setup"; import { findAvailablePort } from "@/lib/ports"; import { runInBackground } from "@/lib/async-operations"; +import { getProject } from "@/lib/projects"; // GET /api/sessions - List all sessions and groups export async function GET() { @@ -181,6 +182,21 @@ export async function POST(request: NextRequest) { const session = queries.getSession(db).get(id) as Session; + // Get project's initial prompt if available + const project = projectId ? getProject(projectId) : null; + const projectInitialPrompt = project?.initial_prompt?.trim(); + const sessionInitialPrompt = initialPrompt?.trim(); + + // Combine prompts: project prompt first, then session prompt + let combinedPrompt: string | undefined; + if (projectInitialPrompt && sessionInitialPrompt) { + combinedPrompt = `${projectInitialPrompt}\n\n${sessionInitialPrompt}`; + } else if (projectInitialPrompt) { + combinedPrompt = projectInitialPrompt; + } else if (sessionInitialPrompt) { + combinedPrompt = sessionInitialPrompt; + } + // Include setup result and initial prompt in response const response: { session: Session; @@ -190,8 +206,8 @@ export async function POST(request: NextRequest) { if (setupResult) { response.setup = setupResult; } - if (initialPrompt?.trim()) { - response.initialPrompt = initialPrompt.trim(); + if (combinedPrompt) { + response.initialPrompt = combinedPrompt; } return NextResponse.json(response, { status: 201 }); diff --git a/components/Projects/ProjectSettingsDialog.tsx b/components/Projects/ProjectSettingsDialog.tsx index 087b98b..03f375c 100644 --- a/components/Projects/ProjectSettingsDialog.tsx +++ b/components/Projects/ProjectSettingsDialog.tsx @@ -10,6 +10,7 @@ import { } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, @@ -67,6 +68,7 @@ export function ProjectSettingsDialog({ const [workingDirectory, setWorkingDirectory] = useState(""); const [agentType, setAgentType] = useState("claude"); const [defaultModel, setDefaultModel] = useState("sonnet"); + const [initialPrompt, setInitialPrompt] = useState(""); const [devServers, setDevServers] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isDetecting, setIsDetecting] = useState(false); @@ -82,6 +84,7 @@ export function ProjectSettingsDialog({ setWorkingDirectory(project.working_directory); setAgentType(project.agent_type); setDefaultModel(project.default_model); + setInitialPrompt(project.initial_prompt || ""); setDevServers( project.devServers.map((ds) => ({ id: ds.id, @@ -190,6 +193,7 @@ export function ProjectSettingsDialog({ workingDirectory, agentType, defaultModel, + initialPrompt: initialPrompt.trim() || null, }); // Handle dev server changes @@ -320,6 +324,22 @@ export function ProjectSettingsDialog({ + {/* Initial Prompt */} +
+ +