Skip to content
Merged
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
47 changes: 47 additions & 0 deletions app/api/git/multi-status/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { NextRequest, NextResponse } from "next/server";
import { getProjectRepositories, getProject } from "@/lib/projects";
import { getMultiRepoGitStatus } from "@/lib/multi-repo-git";
import { expandPath } from "@/lib/git-status";

// GET /api/git/multi-status - Get aggregated git status for a project's repositories
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const projectId = searchParams.get("projectId");
const fallbackPath = searchParams.get("fallbackPath");

if (!projectId && !fallbackPath) {
return NextResponse.json(
{ error: "Either projectId or fallbackPath is required" },
{ status: 400 }
);
}

let repositories: ReturnType<typeof getProjectRepositories> = [];

if (projectId) {
const project = getProject(projectId);
if (!project) {
return NextResponse.json(
{ error: "Project not found" },
{ status: 404 }
);
}
repositories = getProjectRepositories(projectId);
}

// Get aggregated status
const expandedFallback = fallbackPath
? expandPath(fallbackPath)
: undefined;
const status = getMultiRepoGitStatus(repositories, expandedFallback);

return NextResponse.json(status);
} catch (error) {
console.error("Error fetching multi-repo git status:", error);
return NextResponse.json(
{ error: "Failed to fetch git status" },
{ status: 500 }
);
}
}
71 changes: 71 additions & 0 deletions app/api/projects/[id]/repositories/[repoId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { NextRequest, NextResponse } from "next/server";
import {
updateProjectRepository,
deleteProjectRepository,
} from "@/lib/projects";
import { queries, db, type ProjectRepository } from "@/lib/db";

interface RouteParams {
params: Promise<{ id: string; repoId: string }>;
}

// PATCH /api/projects/[id]/repositories/[repoId] - Update a repository
export async function PATCH(request: NextRequest, { params }: RouteParams) {
try {
const { repoId } = await params;

const existing = queries.getProjectRepository(db).get(repoId) as
| (Omit<ProjectRepository, "is_primary"> & { is_primary: number })
| undefined;
if (!existing) {
return NextResponse.json(
{ error: "Repository not found" },
{ status: 404 }
);
}

const body = await request.json();
const { name, path, isPrimary, sortOrder } = body;

const repository = updateProjectRepository(repoId, {
name,
path,
isPrimary,
sortOrder,
});

return NextResponse.json({ repository });
} catch (error) {
console.error("Error updating repository:", error);
return NextResponse.json(
{ error: "Failed to update repository" },
{ status: 500 }
);
}
}

// DELETE /api/projects/[id]/repositories/[repoId] - Delete a repository
export async function DELETE(request: NextRequest, { params }: RouteParams) {
try {
const { repoId } = await params;

const existing = queries.getProjectRepository(db).get(repoId) as
| (Omit<ProjectRepository, "is_primary"> & { is_primary: number })
| undefined;
if (!existing) {
return NextResponse.json(
{ error: "Repository not found" },
{ status: 404 }
);
}

deleteProjectRepository(repoId);
return NextResponse.json({ success: true });
} catch (error) {
console.error("Error deleting repository:", error);
return NextResponse.json(
{ error: "Failed to delete repository" },
{ status: 500 }
);
}
}
74 changes: 74 additions & 0 deletions app/api/projects/[id]/repositories/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { NextRequest, NextResponse } from "next/server";
import {
getProject,
getProjectRepositories,
addProjectRepository,
} from "@/lib/projects";

interface RouteParams {
params: Promise<{ id: string }>;
}

// GET /api/projects/[id]/repositories - List repositories for a project
export async function GET(request: NextRequest, { params }: RouteParams) {
try {
const { id } = await params;
const project = getProject(id);

if (!project) {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}

const repositories = getProjectRepositories(id);
return NextResponse.json({ repositories });
} catch (error) {
console.error("Error fetching repositories:", error);
return NextResponse.json(
{ error: "Failed to fetch repositories" },
{ status: 500 }
);
}
}

// POST /api/projects/[id]/repositories - Add a repository to a project
export async function POST(request: NextRequest, { params }: RouteParams) {
try {
const { id } = await params;
const project = getProject(id);

if (!project) {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}

if (project.is_uncategorized) {
return NextResponse.json(
{ error: "Cannot add repositories to Uncategorized project" },
{ status: 400 }
);
}

const body = await request.json();
const { name, path, isPrimary } = body;

if (!name || !path) {
return NextResponse.json(
{ error: "Name and path are required" },
{ status: 400 }
);
}

const repository = addProjectRepository(id, {
name,
path,
isPrimary,
});

return NextResponse.json({ repository }, { status: 201 });
} catch (error) {
console.error("Error adding repository:", error);
return NextResponse.json(
{ error: "Failed to add repository" },
{ status: 500 }
);
}
}
18 changes: 16 additions & 2 deletions app/api/projects/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,34 @@ 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") {
toggleProjectExpanded(id, expanded);
}

// 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) {
Expand Down
20 changes: 18 additions & 2 deletions app/api/sessions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
Expand All @@ -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 });
Expand Down
2 changes: 1 addition & 1 deletion components/FolderPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function FolderPicker({
: files;

return (
<div className="bg-background fixed inset-0 z-50 flex flex-col">
<div className="bg-background fixed inset-0 z-[100] flex flex-col">
{/* Header */}
<div className="border-border bg-background/95 flex items-center gap-2 border-b p-3 backdrop-blur-sm">
<Button
Expand Down
Loading