@atercates/claude-deck 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +123 -0
- package/app/api/claude/hidden/route.ts +66 -0
- package/app/api/claude/projects/[name]/sessions/route.ts +71 -0
- package/app/api/claude/projects/route.ts +44 -0
- package/app/api/code-search/available/route.ts +12 -0
- package/app/api/code-search/route.ts +47 -0
- package/app/api/dev-servers/[id]/logs/route.ts +23 -0
- package/app/api/dev-servers/[id]/restart/route.ts +20 -0
- package/app/api/dev-servers/[id]/route.ts +51 -0
- package/app/api/dev-servers/[id]/stop/route.ts +20 -0
- package/app/api/dev-servers/detect/route.ts +39 -0
- package/app/api/dev-servers/route.ts +48 -0
- package/app/api/exec/route.ts +60 -0
- package/app/api/files/content/route.ts +76 -0
- package/app/api/files/route.ts +37 -0
- package/app/api/files/upload-temp/route.ts +41 -0
- package/app/api/git/check/route.ts +54 -0
- package/app/api/git/clone/route.ts +99 -0
- package/app/api/git/commit/route.ts +75 -0
- package/app/api/git/discard/route.ts +38 -0
- package/app/api/git/file-content/route.ts +64 -0
- package/app/api/git/history/[hash]/diff/route.ts +38 -0
- package/app/api/git/history/[hash]/route.ts +34 -0
- package/app/api/git/history/route.ts +27 -0
- package/app/api/git/multi-status/route.ts +46 -0
- package/app/api/git/pr/route.ts +164 -0
- package/app/api/git/push/route.ts +64 -0
- package/app/api/git/stage/route.ts +40 -0
- package/app/api/git/status/route.ts +51 -0
- package/app/api/git/unstage/route.ts +46 -0
- package/app/api/groups/[...path]/route.ts +136 -0
- package/app/api/groups/route.ts +93 -0
- package/app/api/orchestrate/spawn/route.ts +45 -0
- package/app/api/orchestrate/workers/[id]/route.ts +89 -0
- package/app/api/orchestrate/workers/route.ts +31 -0
- package/app/api/projects/[id]/detect/route.ts +27 -0
- package/app/api/projects/[id]/dev-servers/[dsId]/route.ts +66 -0
- package/app/api/projects/[id]/dev-servers/route.ts +51 -0
- package/app/api/projects/[id]/repositories/[repoId]/route.ts +67 -0
- package/app/api/projects/[id]/repositories/route.ts +74 -0
- package/app/api/projects/[id]/route.ts +108 -0
- package/app/api/projects/detect/route.ts +33 -0
- package/app/api/projects/route.ts +59 -0
- package/app/api/sessions/[id]/claude-session/route.ts +42 -0
- package/app/api/sessions/[id]/fork/route.ts +74 -0
- package/app/api/sessions/[id]/mcp-config/route.ts +34 -0
- package/app/api/sessions/[id]/messages/route.ts +60 -0
- package/app/api/sessions/[id]/pr/route.ts +188 -0
- package/app/api/sessions/[id]/preview/route.ts +42 -0
- package/app/api/sessions/[id]/route.ts +229 -0
- package/app/api/sessions/[id]/send-keys/route.ts +119 -0
- package/app/api/sessions/[id]/summarize/route.ts +331 -0
- package/app/api/sessions/init-script/route.ts +84 -0
- package/app/api/sessions/route.ts +209 -0
- package/app/api/sessions/status/route.ts +237 -0
- package/app/api/system/route.ts +9 -0
- package/app/api/tmux/kill-all/route.ts +57 -0
- package/app/api/tmux/rename/route.ts +30 -0
- package/app/globals.css +174 -0
- package/app/icon.svg +11 -0
- package/app/layout.tsx +122 -0
- package/app/page.tsx +629 -0
- package/components/ChatMessage.tsx +65 -0
- package/components/ChatView.tsx +276 -0
- package/components/ClaudeProjects/ClaudeProjectCard.tsx +195 -0
- package/components/ClaudeProjects/ClaudeProjectsSection.tsx +89 -0
- package/components/ClaudeProjects/ClaudeSessionCard.tsx +100 -0
- package/components/ClaudeProjects/index.ts +1 -0
- package/components/CodeSearch/CodeSearchResults.tsx +177 -0
- package/components/ConductorPanel.tsx +256 -0
- package/components/DevServers/DevServerCard.tsx +311 -0
- package/components/DevServers/DevServersSection.tsx +91 -0
- package/components/DevServers/ServerLogsModal.tsx +151 -0
- package/components/DevServers/StartServerDialog.tsx +359 -0
- package/components/DevServers/index.ts +4 -0
- package/components/DiffViewer/DiffModal.tsx +151 -0
- package/components/DiffViewer/UnifiedDiff.tsx +185 -0
- package/components/DiffViewer/index.tsx +2 -0
- package/components/DirectoryPicker.tsx +355 -0
- package/components/FileExplorer/FileEditor.tsx +276 -0
- package/components/FileExplorer/FileTabs.tsx +118 -0
- package/components/FileExplorer/FileTree.tsx +214 -0
- package/components/FileExplorer/HtmlRenderer.tsx +16 -0
- package/components/FileExplorer/MarkdownRenderer.tsx +18 -0
- package/components/FileExplorer/index.tsx +520 -0
- package/components/FilePicker.tsx +339 -0
- package/components/FolderPicker.tsx +201 -0
- package/components/GitDrawer/FileEditDialog.tsx +400 -0
- package/components/GitDrawer/index.tsx +464 -0
- package/components/GitPanel/CommitForm.tsx +205 -0
- package/components/GitPanel/CommitHistory.tsx +174 -0
- package/components/GitPanel/CommitItem.tsx +196 -0
- package/components/GitPanel/FileChanges.tsx +414 -0
- package/components/GitPanel/GitPanelTabs.tsx +39 -0
- package/components/GitPanel/index.tsx +817 -0
- package/components/MessageInput.tsx +82 -0
- package/components/NewClaudeSessionDialog.tsx +166 -0
- package/components/NewSessionDialog/AdvancedSettings.tsx +78 -0
- package/components/NewSessionDialog/AgentSelector.tsx +37 -0
- package/components/NewSessionDialog/CreatingOverlay.tsx +94 -0
- package/components/NewSessionDialog/NewSessionDialog.types.ts +136 -0
- package/components/NewSessionDialog/ProjectSelector.tsx +146 -0
- package/components/NewSessionDialog/WorkingDirectoryInput.tsx +55 -0
- package/components/NewSessionDialog/WorktreeSection.tsx +92 -0
- package/components/NewSessionDialog/hooks/useNewSessionForm.ts +370 -0
- package/components/NewSessionDialog/index.tsx +106 -0
- package/components/NotificationSettings.tsx +127 -0
- package/components/PRCreationModal.tsx +272 -0
- package/components/Pane/DesktopTabBar.tsx +353 -0
- package/components/Pane/MobileTabBar.tsx +210 -0
- package/components/Pane/OpenInVSCode.tsx +69 -0
- package/components/Pane/PaneSkeletons.tsx +57 -0
- package/components/Pane/index.tsx +558 -0
- package/components/PaneLayout.tsx +60 -0
- package/components/Projects/DevServersSection.tsx +140 -0
- package/components/Projects/DirectoryField.tsx +92 -0
- package/components/Projects/NewProjectDialog.tsx +188 -0
- package/components/Projects/NewProjectDialog.types.ts +46 -0
- package/components/Projects/ProjectCard.tsx +276 -0
- package/components/Projects/ProjectSettingsDialog.tsx +811 -0
- package/components/Projects/hooks/useNewProjectForm.ts +249 -0
- package/components/Projects/index.ts +3 -0
- package/components/Providers.tsx +49 -0
- package/components/QuickSwitcher.tsx +306 -0
- package/components/SessionList/KillAllConfirm.tsx +46 -0
- package/components/SessionList/SelectionToolbar.tsx +164 -0
- package/components/SessionList/SessionList.types.ts +37 -0
- package/components/SessionList/SessionListHeader.tsx +71 -0
- package/components/SessionList/hooks/useSessionListMutations.ts +269 -0
- package/components/SessionList/index.tsx +189 -0
- package/components/ShellDrawer/index.tsx +106 -0
- package/components/SidebarFooter.tsx +55 -0
- package/components/Terminal/KeybarToggleButton.tsx +45 -0
- package/components/Terminal/ScrollToBottomButton.tsx +32 -0
- package/components/Terminal/SearchBar.tsx +71 -0
- package/components/Terminal/TerminalToolbar.tsx +551 -0
- package/components/Terminal/VirtualKeyboard.tsx +711 -0
- package/components/Terminal/constants.ts +20 -0
- package/components/Terminal/hooks/index.ts +5 -0
- package/components/Terminal/hooks/resize-handlers.ts +140 -0
- package/components/Terminal/hooks/terminal-init.ts +151 -0
- package/components/Terminal/hooks/touch-scroll.ts +155 -0
- package/components/Terminal/hooks/useTerminalConnection.ts +282 -0
- package/components/Terminal/hooks/useTerminalConnection.types.ts +39 -0
- package/components/Terminal/hooks/useTerminalSearch.ts +103 -0
- package/components/Terminal/hooks/websocket-connection.ts +274 -0
- package/components/Terminal/index.tsx +320 -0
- package/components/ThemeToggle.tsx +168 -0
- package/components/TmuxSessions.tsx +132 -0
- package/components/ToolCallDisplay.tsx +71 -0
- package/components/WorkerCard.tsx +245 -0
- package/components/a/ABadge.tsx +115 -0
- package/components/a/AButton.tsx +163 -0
- package/components/a/ADialog.tsx +93 -0
- package/components/a/ADropdownMenu.tsx +279 -0
- package/components/a/AIconButton.tsx +190 -0
- package/components/a/ASheet.tsx +150 -0
- package/components/a/ATooltip.tsx +77 -0
- package/components/a/index.ts +64 -0
- package/components/mobile/SwipeSidebar.tsx +122 -0
- package/components/ui/badge.tsx +41 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/context-menu.tsx +197 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/input.tsx +21 -0
- package/components/ui/scroll-area.tsx +52 -0
- package/components/ui/select.tsx +159 -0
- package/components/ui/skeleton.tsx +111 -0
- package/components/ui/switch.tsx +31 -0
- package/components/ui/textarea.tsx +21 -0
- package/components/ui/tooltip.tsx +32 -0
- package/components/views/DesktopView.tsx +244 -0
- package/components/views/MobileView.tsx +110 -0
- package/components/views/types.ts +75 -0
- package/contexts/PaneContext.tsx +336 -0
- package/data/claude/index.ts +9 -0
- package/data/claude/keys.ts +6 -0
- package/data/claude/queries.ts +120 -0
- package/data/claude/useClaudeUpdates.ts +37 -0
- package/data/code-search/index.ts +2 -0
- package/data/code-search/keys.ts +7 -0
- package/data/code-search/queries.ts +61 -0
- package/data/dev-servers/index.ts +8 -0
- package/data/dev-servers/keys.ts +4 -0
- package/data/dev-servers/queries.ts +104 -0
- package/data/files/index.ts +3 -0
- package/data/files/keys.ts +4 -0
- package/data/files/queries.ts +25 -0
- package/data/git/keys.ts +15 -0
- package/data/git/queries.ts +395 -0
- package/data/groups/index.ts +1 -0
- package/data/groups/mutations.ts +95 -0
- package/data/projects/index.ts +10 -0
- package/data/projects/keys.ts +4 -0
- package/data/projects/queries.ts +193 -0
- package/data/repositories/index.ts +7 -0
- package/data/repositories/keys.ts +5 -0
- package/data/repositories/queries.ts +122 -0
- package/data/sessions/index.ts +12 -0
- package/data/sessions/keys.ts +8 -0
- package/data/sessions/queries.ts +218 -0
- package/data/statuses/index.ts +1 -0
- package/data/statuses/queries.ts +69 -0
- package/hooks/useCopyToClipboard.ts +48 -0
- package/hooks/useDevServersManager.ts +73 -0
- package/hooks/useDirectoryBrowser.ts +90 -0
- package/hooks/useDrawerAnimation.ts +27 -0
- package/hooks/useFileDrop.ts +87 -0
- package/hooks/useFileEditor.ts +184 -0
- package/hooks/useGroups.ts +37 -0
- package/hooks/useHomePath.ts +34 -0
- package/hooks/useKeyRepeat.ts +55 -0
- package/hooks/useKeybarVisibility.ts +42 -0
- package/hooks/useNotifications.ts +257 -0
- package/hooks/useProjects.ts +53 -0
- package/hooks/useSessionStatuses.ts +30 -0
- package/hooks/useSessions.ts +86 -0
- package/hooks/useSpeechRecognition.ts +124 -0
- package/hooks/useViewport.ts +32 -0
- package/hooks/useViewportHeight.ts +50 -0
- package/lib/async-operations.ts +35 -0
- package/lib/banner.ts +81 -0
- package/lib/claude/jsonl-cache.ts +86 -0
- package/lib/claude/jsonl-reader.ts +271 -0
- package/lib/claude/process-manager.ts +278 -0
- package/lib/claude/stream-parser.ts +173 -0
- package/lib/claude/types.ts +154 -0
- package/lib/claude/watcher.ts +71 -0
- package/lib/client/session-registry.ts +111 -0
- package/lib/code-search.ts +121 -0
- package/lib/db/index.ts +48 -0
- package/lib/db/migrations.ts +45 -0
- package/lib/db/queries.ts +460 -0
- package/lib/db/schema.ts +114 -0
- package/lib/db/types.ts +92 -0
- package/lib/db.ts +2 -0
- package/lib/dev-servers.ts +509 -0
- package/lib/diff-parser.ts +221 -0
- package/lib/env-setup.ts +285 -0
- package/lib/file-upload.ts +34 -0
- package/lib/file-utils.ts +50 -0
- package/lib/files.ts +207 -0
- package/lib/git-history.ts +294 -0
- package/lib/git-status.ts +391 -0
- package/lib/git.ts +257 -0
- package/lib/mcp-config.ts +81 -0
- package/lib/multi-repo-git.ts +179 -0
- package/lib/notifications.ts +219 -0
- package/lib/orchestration.ts +448 -0
- package/lib/panes.ts +232 -0
- package/lib/ports.ts +97 -0
- package/lib/pr-generation.ts +307 -0
- package/lib/pr.ts +234 -0
- package/lib/projects.ts +578 -0
- package/lib/providers/registry.ts +70 -0
- package/lib/providers.ts +121 -0
- package/lib/query-client.ts +14 -0
- package/lib/rangeSelectionUtils.ts +65 -0
- package/lib/status-detector.ts +375 -0
- package/lib/terminal-themes.ts +265 -0
- package/lib/theme-config.ts +327 -0
- package/lib/utils.ts +6 -0
- package/lib/worktrees.ts +262 -0
- package/mcp/orchestration-server.ts +438 -0
- package/package.json +139 -0
- package/postcss.config.mjs +7 -0
- package/public/icon.svg +10 -0
- package/public/icons/icon-128x128.png +0 -0
- package/public/icons/icon-144x144.png +0 -0
- package/public/icons/icon-152x152.png +0 -0
- package/public/icons/icon-192x192.png +0 -0
- package/public/icons/icon-384x384.png +0 -0
- package/public/icons/icon-512x512.png +0 -0
- package/public/icons/icon-72x72.png +0 -0
- package/public/icons/icon-96x96.png +0 -0
- package/public/manifest.json +61 -0
- package/public/sw.js +64 -0
- package/scripts/agent-os +91 -0
- package/scripts/install.sh +48 -0
- package/scripts/lib/ai-clis.sh +132 -0
- package/scripts/lib/commands.sh +487 -0
- package/scripts/lib/common.sh +89 -0
- package/scripts/lib/prerequisites.sh +462 -0
- package/scripts/setup.sh +134 -0
- package/server.ts +155 -0
- package/stores/fileOpen.ts +26 -0
- package/stores/index.ts +1 -0
- package/stores/initialPrompt.ts +24 -0
- package/stores/sessionSelection.ts +48 -0
- package/styles/themes.css +603 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { readFileContent, writeFileContent } from "@/lib/files";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GET /api/files/content?path=...
|
|
6
|
+
* Read file contents
|
|
7
|
+
*/
|
|
8
|
+
export async function GET(request: NextRequest) {
|
|
9
|
+
try {
|
|
10
|
+
const searchParams = request.nextUrl.searchParams;
|
|
11
|
+
const path = searchParams.get("path");
|
|
12
|
+
|
|
13
|
+
if (!path) {
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ error: "Path parameter is required" },
|
|
16
|
+
{ status: 400 }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Expand ~ to home directory
|
|
21
|
+
const expandedPath = path.replace(/^~/, process.env.HOME || "");
|
|
22
|
+
|
|
23
|
+
const result = readFileContent(expandedPath);
|
|
24
|
+
|
|
25
|
+
return NextResponse.json({
|
|
26
|
+
...result,
|
|
27
|
+
path: expandedPath,
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("Error reading file:", error);
|
|
31
|
+
return NextResponse.json(
|
|
32
|
+
{ error: error instanceof Error ? error.message : "Failed to read file" },
|
|
33
|
+
{ status: 500 }
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* POST /api/files/content
|
|
40
|
+
* Write file contents
|
|
41
|
+
*/
|
|
42
|
+
export async function POST(request: NextRequest) {
|
|
43
|
+
try {
|
|
44
|
+
const body = await request.json();
|
|
45
|
+
const { path, content } = body;
|
|
46
|
+
|
|
47
|
+
if (!path) {
|
|
48
|
+
return NextResponse.json({ error: "Path is required" }, { status: 400 });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (content === undefined) {
|
|
52
|
+
return NextResponse.json(
|
|
53
|
+
{ error: "Content is required" },
|
|
54
|
+
{ status: 400 }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Expand ~ to home directory
|
|
59
|
+
const expandedPath = path.replace(/^~/, process.env.HOME || "");
|
|
60
|
+
|
|
61
|
+
const result = writeFileContent(expandedPath, content);
|
|
62
|
+
|
|
63
|
+
return NextResponse.json({
|
|
64
|
+
...result,
|
|
65
|
+
path: expandedPath,
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("Error writing file:", error);
|
|
69
|
+
return NextResponse.json(
|
|
70
|
+
{
|
|
71
|
+
error: error instanceof Error ? error.message : "Failed to write file",
|
|
72
|
+
},
|
|
73
|
+
{ status: 500 }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { listDirectory } from "@/lib/files";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GET /api/files?path=...&recursive=true
|
|
6
|
+
* List directory contents
|
|
7
|
+
*/
|
|
8
|
+
export async function GET(request: NextRequest) {
|
|
9
|
+
try {
|
|
10
|
+
const searchParams = request.nextUrl.searchParams;
|
|
11
|
+
const path = searchParams.get("path");
|
|
12
|
+
const recursive = searchParams.get("recursive") === "true";
|
|
13
|
+
|
|
14
|
+
if (!path) {
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: "Path parameter is required" },
|
|
17
|
+
{ status: 400 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Expand ~ to home directory
|
|
22
|
+
const expandedPath = path.replace(/^~/, process.env.HOME || "");
|
|
23
|
+
|
|
24
|
+
const files = listDirectory(expandedPath, {
|
|
25
|
+
recursive,
|
|
26
|
+
maxDepth: recursive ? 2 : 1,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return NextResponse.json({ files, path: expandedPath });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Error listing directory:", error);
|
|
32
|
+
return NextResponse.json(
|
|
33
|
+
{ error: "Failed to list directory" },
|
|
34
|
+
{ status: 500 }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
|
|
6
|
+
export async function POST(request: Request) {
|
|
7
|
+
try {
|
|
8
|
+
const { filename, base64, mimeType } = await request.json();
|
|
9
|
+
|
|
10
|
+
if (!base64) {
|
|
11
|
+
return NextResponse.json({ error: "No image data" }, { status: 400 });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Create temp directory for screenshots if it doesn't exist
|
|
15
|
+
const tempDir = path.join(os.tmpdir(), "claude-deck-screenshots");
|
|
16
|
+
if (!fs.existsSync(tempDir)) {
|
|
17
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Generate unique filename
|
|
21
|
+
const ext = mimeType?.split("/")[1] || "png";
|
|
22
|
+
const safeName = filename?.replace(/[^a-zA-Z0-9.-]/g, "_") || "screenshot";
|
|
23
|
+
const uniqueName = `${Date.now()}-${safeName}`;
|
|
24
|
+
const finalName = uniqueName.endsWith(`.${ext}`)
|
|
25
|
+
? uniqueName
|
|
26
|
+
: `${uniqueName}.${ext}`;
|
|
27
|
+
const filePath = path.join(tempDir, finalName);
|
|
28
|
+
|
|
29
|
+
// Decode base64 and write file
|
|
30
|
+
const buffer = Buffer.from(base64, "base64");
|
|
31
|
+
fs.writeFileSync(filePath, buffer);
|
|
32
|
+
|
|
33
|
+
return NextResponse.json({ path: filePath });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error("Upload error:", error);
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ error: "Failed to save image" },
|
|
38
|
+
{ status: 500 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import {
|
|
3
|
+
isGitRepo,
|
|
4
|
+
getBranches,
|
|
5
|
+
getDefaultBranch,
|
|
6
|
+
getCurrentBranch,
|
|
7
|
+
} from "@/lib/git";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* POST /api/git/check
|
|
11
|
+
* Check if a path is a git repository and return branch info
|
|
12
|
+
*/
|
|
13
|
+
export async function POST(request: NextRequest) {
|
|
14
|
+
try {
|
|
15
|
+
const body = await request.json();
|
|
16
|
+
const { path: dirPath } = body;
|
|
17
|
+
|
|
18
|
+
if (!dirPath) {
|
|
19
|
+
return NextResponse.json({ error: "Path is required" }, { status: 400 });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if it's a git repo
|
|
23
|
+
const isRepo = await isGitRepo(dirPath);
|
|
24
|
+
|
|
25
|
+
if (!isRepo) {
|
|
26
|
+
return NextResponse.json({
|
|
27
|
+
isGitRepo: false,
|
|
28
|
+
branches: [],
|
|
29
|
+
defaultBranch: null,
|
|
30
|
+
currentBranch: null,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Get branch info
|
|
35
|
+
const [branches, defaultBranch, currentBranch] = await Promise.all([
|
|
36
|
+
getBranches(dirPath),
|
|
37
|
+
getDefaultBranch(dirPath),
|
|
38
|
+
getCurrentBranch(dirPath),
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
return NextResponse.json({
|
|
42
|
+
isGitRepo: true,
|
|
43
|
+
branches,
|
|
44
|
+
defaultBranch,
|
|
45
|
+
currentBranch,
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("Error checking git repo:", error);
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: "Failed to check git repository" },
|
|
51
|
+
{ status: 500 }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as fs from "fs/promises";
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* POST /api/git/clone
|
|
11
|
+
* Clone a git repository into a target directory
|
|
12
|
+
*/
|
|
13
|
+
export async function POST(request: NextRequest) {
|
|
14
|
+
try {
|
|
15
|
+
const body = await request.json();
|
|
16
|
+
const { url, directory } = body;
|
|
17
|
+
|
|
18
|
+
if (!url) {
|
|
19
|
+
return NextResponse.json(
|
|
20
|
+
{ error: "Repository URL is required" },
|
|
21
|
+
{ status: 400 }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!directory) {
|
|
26
|
+
return NextResponse.json(
|
|
27
|
+
{ error: "Target directory is required" },
|
|
28
|
+
{ status: 400 }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Resolve ~ to home directory
|
|
33
|
+
const resolvedDir = directory.replace(/^~/, process.env.HOME || "");
|
|
34
|
+
|
|
35
|
+
// Verify parent directory exists
|
|
36
|
+
try {
|
|
37
|
+
await fs.access(resolvedDir);
|
|
38
|
+
} catch {
|
|
39
|
+
return NextResponse.json(
|
|
40
|
+
{ error: `Directory does not exist: ${directory}` },
|
|
41
|
+
{ status: 400 }
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Extract repo name from URL for the clone target
|
|
46
|
+
const repoName = extractRepoName(url);
|
|
47
|
+
if (!repoName) {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{ error: "Could not determine repository name from URL" },
|
|
50
|
+
{ status: 400 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const clonePath = path.join(resolvedDir, repoName);
|
|
55
|
+
|
|
56
|
+
// Check if target already exists
|
|
57
|
+
try {
|
|
58
|
+
await fs.access(clonePath);
|
|
59
|
+
return NextResponse.json(
|
|
60
|
+
{ error: `Directory already exists: ${clonePath}` },
|
|
61
|
+
{ status: 409 }
|
|
62
|
+
);
|
|
63
|
+
} catch {
|
|
64
|
+
// Good - doesn't exist yet
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Clone the repository
|
|
68
|
+
const { stderr } = await execAsync(`git clone "${url}" "${clonePath}"`, {
|
|
69
|
+
timeout: 120000,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// git clone outputs progress to stderr, not an error
|
|
73
|
+
if (stderr && stderr.includes("fatal:")) {
|
|
74
|
+
return NextResponse.json({ error: stderr.trim() }, { status: 500 });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return NextResponse.json({
|
|
78
|
+
path: clonePath,
|
|
79
|
+
name: repoName,
|
|
80
|
+
});
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const message =
|
|
83
|
+
error instanceof Error ? error.message : "Failed to clone repository";
|
|
84
|
+
console.error("Error cloning repository:", message);
|
|
85
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function extractRepoName(url: string): string | null {
|
|
90
|
+
// https://github.com/user/repo.git or https://github.com/user/repo
|
|
91
|
+
// git@github.com:user/repo.git
|
|
92
|
+
const match = url.match(
|
|
93
|
+
/(?:[\w.-]+\/([\w.-]+?)(?:\.git)?$|:([\w.-]+\/)([\w.-]+?)(?:\.git)?$)/
|
|
94
|
+
);
|
|
95
|
+
if (match) {
|
|
96
|
+
return match[1] || match[3] || null;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import {
|
|
3
|
+
commit,
|
|
4
|
+
isGitRepo,
|
|
5
|
+
isMainBranch,
|
|
6
|
+
createBranch,
|
|
7
|
+
getGitStatus,
|
|
8
|
+
expandPath,
|
|
9
|
+
} from "@/lib/git-status";
|
|
10
|
+
|
|
11
|
+
export async function POST(request: NextRequest) {
|
|
12
|
+
try {
|
|
13
|
+
const body = await request.json();
|
|
14
|
+
const {
|
|
15
|
+
path: rawPath,
|
|
16
|
+
message,
|
|
17
|
+
branchName,
|
|
18
|
+
} = body as {
|
|
19
|
+
path: string;
|
|
20
|
+
message: string;
|
|
21
|
+
branchName?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (!rawPath) {
|
|
25
|
+
return NextResponse.json({ error: "Path is required" }, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!message) {
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ error: "Commit message is required" },
|
|
31
|
+
{ status: 400 }
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const path = expandPath(rawPath);
|
|
36
|
+
|
|
37
|
+
if (!isGitRepo(path)) {
|
|
38
|
+
return NextResponse.json(
|
|
39
|
+
{ error: "Not a git repository" },
|
|
40
|
+
{ status: 400 }
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if there are staged changes
|
|
45
|
+
const status = getGitStatus(path);
|
|
46
|
+
if (status.staged.length === 0) {
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ error: "No staged changes to commit" },
|
|
49
|
+
{ status: 400 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create new branch if on main/master and branch name provided
|
|
54
|
+
let newBranch = false;
|
|
55
|
+
if (branchName && isMainBranch(path)) {
|
|
56
|
+
createBranch(path, branchName);
|
|
57
|
+
newBranch = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Commit
|
|
61
|
+
const output = commit(path, message);
|
|
62
|
+
|
|
63
|
+
return NextResponse.json({
|
|
64
|
+
success: true,
|
|
65
|
+
output,
|
|
66
|
+
newBranch,
|
|
67
|
+
branchName: newBranch ? branchName : undefined,
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{ error: error instanceof Error ? error.message : "Failed to commit" },
|
|
72
|
+
{ status: 500 }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { discardChanges, isGitRepo, expandPath } from "@/lib/git-status";
|
|
3
|
+
|
|
4
|
+
export async function POST(request: NextRequest) {
|
|
5
|
+
try {
|
|
6
|
+
const body = await request.json();
|
|
7
|
+
const { path: rawPath, file } = body as { path: string; file: string };
|
|
8
|
+
|
|
9
|
+
if (!rawPath) {
|
|
10
|
+
return NextResponse.json({ error: "Path is required" }, { status: 400 });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!file) {
|
|
14
|
+
return NextResponse.json({ error: "File is required" }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const path = expandPath(rawPath);
|
|
18
|
+
|
|
19
|
+
if (!isGitRepo(path)) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: "Not a git repository" },
|
|
22
|
+
{ status: 400 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
discardChanges(path, file);
|
|
27
|
+
|
|
28
|
+
return NextResponse.json({ success: true });
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
{
|
|
32
|
+
error:
|
|
33
|
+
error instanceof Error ? error.message : "Failed to discard changes",
|
|
34
|
+
},
|
|
35
|
+
{ status: 500 }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import { expandPath } from "@/lib/git-status";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/git/file-content?path=...&file=...
|
|
7
|
+
* Get file content from git HEAD (original version before changes)
|
|
8
|
+
*/
|
|
9
|
+
export async function GET(request: NextRequest) {
|
|
10
|
+
try {
|
|
11
|
+
const searchParams = request.nextUrl.searchParams;
|
|
12
|
+
const path = searchParams.get("path");
|
|
13
|
+
const file = searchParams.get("file");
|
|
14
|
+
|
|
15
|
+
if (!path) {
|
|
16
|
+
return NextResponse.json(
|
|
17
|
+
{ error: "Path parameter is required" },
|
|
18
|
+
{ status: 400 }
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!file) {
|
|
23
|
+
return NextResponse.json(
|
|
24
|
+
{ error: "File parameter is required" },
|
|
25
|
+
{ status: 400 }
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const expandedPath = expandPath(path);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// Get file content from HEAD
|
|
33
|
+
const content = execSync(`git show HEAD:"${file}"`, {
|
|
34
|
+
cwd: expandedPath,
|
|
35
|
+
encoding: "utf-8",
|
|
36
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return NextResponse.json({ content });
|
|
40
|
+
} catch (gitError: unknown) {
|
|
41
|
+
// File might be new (not in HEAD)
|
|
42
|
+
const errorMessage =
|
|
43
|
+
gitError instanceof Error ? gitError.message : String(gitError);
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
errorMessage.includes("does not exist") ||
|
|
47
|
+
errorMessage.includes("fatal:")
|
|
48
|
+
) {
|
|
49
|
+
return NextResponse.json({ content: "", isNew: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw gitError;
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error("Error getting file content from git:", error);
|
|
56
|
+
return NextResponse.json(
|
|
57
|
+
{
|
|
58
|
+
error:
|
|
59
|
+
error instanceof Error ? error.message : "Failed to get file content",
|
|
60
|
+
},
|
|
61
|
+
{ status: 500 }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getCommitFileDiff } from "@/lib/git-history";
|
|
3
|
+
|
|
4
|
+
interface RouteParams {
|
|
5
|
+
params: Promise<{ hash: string }>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function GET(request: NextRequest, { params }: RouteParams) {
|
|
9
|
+
try {
|
|
10
|
+
const { hash } = await params;
|
|
11
|
+
const searchParams = request.nextUrl.searchParams;
|
|
12
|
+
const path = searchParams.get("path");
|
|
13
|
+
const file = searchParams.get("file");
|
|
14
|
+
|
|
15
|
+
if (!path) {
|
|
16
|
+
return NextResponse.json(
|
|
17
|
+
{ error: "Missing path parameter" },
|
|
18
|
+
{ status: 400 }
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!file) {
|
|
23
|
+
return NextResponse.json(
|
|
24
|
+
{ error: "Missing file parameter" },
|
|
25
|
+
{ status: 400 }
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const diff = getCommitFileDiff(path, hash, file);
|
|
30
|
+
return NextResponse.json({ diff });
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error("Error getting commit file diff:", error);
|
|
33
|
+
return NextResponse.json(
|
|
34
|
+
{ error: "Failed to get commit file diff" },
|
|
35
|
+
{ status: 500 }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getCommitDetail } from "@/lib/git-history";
|
|
3
|
+
|
|
4
|
+
interface RouteParams {
|
|
5
|
+
params: Promise<{ hash: string }>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function GET(request: NextRequest, { params }: RouteParams) {
|
|
9
|
+
try {
|
|
10
|
+
const { hash } = await params;
|
|
11
|
+
const searchParams = request.nextUrl.searchParams;
|
|
12
|
+
const path = searchParams.get("path");
|
|
13
|
+
|
|
14
|
+
if (!path) {
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: "Missing path parameter" },
|
|
17
|
+
{ status: 400 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const commit = getCommitDetail(path, hash);
|
|
22
|
+
if (!commit) {
|
|
23
|
+
return NextResponse.json({ error: "Commit not found" }, { status: 404 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return NextResponse.json({ commit });
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("Error getting commit detail:", error);
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ error: "Failed to get commit detail" },
|
|
31
|
+
{ status: 500 }
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getCommitHistory } from "@/lib/git-history";
|
|
3
|
+
|
|
4
|
+
export async function GET(request: NextRequest) {
|
|
5
|
+
try {
|
|
6
|
+
const searchParams = request.nextUrl.searchParams;
|
|
7
|
+
const path = searchParams.get("path");
|
|
8
|
+
const limitStr = searchParams.get("limit");
|
|
9
|
+
const limit = limitStr ? parseInt(limitStr, 10) : 30;
|
|
10
|
+
|
|
11
|
+
if (!path) {
|
|
12
|
+
return NextResponse.json(
|
|
13
|
+
{ error: "Missing path parameter" },
|
|
14
|
+
{ status: 400 }
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const commits = getCommitHistory(path, limit);
|
|
19
|
+
return NextResponse.json({ commits });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error("Error getting commit history:", error);
|
|
22
|
+
return NextResponse.json(
|
|
23
|
+
{ error: "Failed to get commit history" },
|
|
24
|
+
{ status: 500 }
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getProjectRepositories, getProject } from "@/lib/projects";
|
|
3
|
+
import { getMultiRepoGitStatus } from "@/lib/multi-repo-git";
|
|
4
|
+
import { expandPath } from "@/lib/git-status";
|
|
5
|
+
import type { ProjectRepository } from "@/lib/db";
|
|
6
|
+
|
|
7
|
+
export async function GET(request: NextRequest) {
|
|
8
|
+
try {
|
|
9
|
+
const { searchParams } = new URL(request.url);
|
|
10
|
+
const projectId = searchParams.get("projectId");
|
|
11
|
+
const fallbackPath = searchParams.get("fallbackPath");
|
|
12
|
+
|
|
13
|
+
if (!projectId && !fallbackPath) {
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ error: "Either projectId or fallbackPath is required" },
|
|
16
|
+
{ status: 400 }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let repositories: ProjectRepository[] = [];
|
|
21
|
+
|
|
22
|
+
if (projectId) {
|
|
23
|
+
const project = await getProject(projectId);
|
|
24
|
+
if (!project) {
|
|
25
|
+
return NextResponse.json(
|
|
26
|
+
{ error: "Project not found" },
|
|
27
|
+
{ status: 404 }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
repositories = await getProjectRepositories(projectId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const expandedFallback = fallbackPath
|
|
34
|
+
? expandPath(fallbackPath)
|
|
35
|
+
: undefined;
|
|
36
|
+
const status = getMultiRepoGitStatus(repositories, expandedFallback);
|
|
37
|
+
|
|
38
|
+
return NextResponse.json(status);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Error fetching multi-repo git status:", error);
|
|
41
|
+
return NextResponse.json(
|
|
42
|
+
{ error: "Failed to fetch git status" },
|
|
43
|
+
{ status: 500 }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|