Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
286c0a9
Normalize message type handling in transformToClaudeFormat function
Gkrumbach07 Oct 17, 2025
258a8a8
Enhance session management and message formatting in backend handlers
Gkrumbach07 Oct 29, 2025
0056c34
Refactor message transformation for Claude SDK control protocol
Gkrumbach07 Oct 29, 2025
62b2eaa
Enhance session continuation logic and message transformation for Cla…
Gkrumbach07 Oct 29, 2025
ed509d3
Update message transformation logic in transformToClaudeFormat function
Gkrumbach07 Oct 29, 2025
c396658
Refactor session handling and client connection logic in ClaudeCodeAd…
Gkrumbach07 Oct 29, 2025
d888258
Enhance client connection and session restoration logic in ClaudeCode…
Gkrumbach07 Oct 29, 2025
0075cd3
Refactor client connection and session restoration in ClaudeCodeAdapter
Gkrumbach07 Oct 29, 2025
2e059be
Refactor session continuation and client connection in ClaudeCodeAdapter
Gkrumbach07 Oct 29, 2025
5bf7d2c
Add hardcoded message history for testing in ClaudeCodeAdapter
Gkrumbach07 Oct 29, 2025
be6e6fc
Refactor commented-out tool usage structure in ClaudeCodeAdapter
Gkrumbach07 Oct 29, 2025
0e14349
Refactor commented-out tool result structure in ClaudeCodeAdapter
Gkrumbach07 Oct 29, 2025
2f1d155
Remove GetSessionMessagesClaudeFormat and update session continuation…
Gkrumbach07 Oct 29, 2025
9db44cf
Update session mount path for Claude state persistence
Gkrumbach07 Oct 29, 2025
bf5003a
Enhance session resumption logic in ClaudeCodeAdapter
Gkrumbach07 Oct 29, 2025
333349b
Add PATCH endpoint for agentic sessions and implement session patchin…
Gkrumbach07 Oct 29, 2025
19a7ea5
Enhance role permissions for AgenticSession updates
Gkrumbach07 Oct 29, 2025
c6dbdf5
Refactor session handling in StartSession and SystemMessage components
Gkrumbach07 Oct 29, 2025
ffdef3d
Refactor SystemMessage component to simplify props and rendering logic
Gkrumbach07 Oct 29, 2025
0ab368f
Enhance message handling and debugging features in session components
Gkrumbach07 Oct 30, 2025
6f6ecf1
Refactor MessagesTab to enhance debug message settings and UI layout
Gkrumbach07 Oct 30, 2025
7388a2c
Refactor MessagesTab and OverviewTab for improved message handling an…
Gkrumbach07 Oct 30, 2025
21ab0fc
Enhance session continuation logic to support headless sessions
Gkrumbach07 Oct 31, 2025
6ea325e
Refactor session cleanup and interactivity handling in session manage…
Gkrumbach07 Oct 31, 2025
900675f
Refactor SessionActions component to remove interactive prop
Gkrumbach07 Oct 31, 2025
60bd82d
Enhance message payload handling in ProjectSessionDetailPage and Mess…
Gkrumbach07 Oct 31, 2025
6cf2a95
Refactor system message handling in ProjectSessionDetailPage and Syst…
Gkrumbach07 Oct 31, 2025
fe6f1ae
Refactor payload handling in ProjectSessionDetailPage and SystemMessa…
Gkrumbach07 Oct 31, 2025
e4f74d5
Update system message handling in ProjectSessionDetailPage to ensure …
Gkrumbach07 Oct 31, 2025
ac0908f
Refactor message extraction logic in ProjectSessionDetailPage for imp…
Gkrumbach07 Oct 31, 2025
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
34 changes: 31 additions & 3 deletions components/backend/git/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type ProjectSettings struct {
type DiffSummary struct {
TotalAdded int `json:"total_added"`
TotalRemoved int `json:"total_removed"`
FilesAdded int `json:"files_added"`
FilesRemoved int `json:"files_removed"`
}

// getProjectSettings retrieves the ProjectSettings CR for a project using the provided dynamic client
Expand Down Expand Up @@ -871,7 +873,7 @@ func DiffRepo(ctx context.Context, repoDir string) (*DiffSummary, error) {

summary := &DiffSummary{}

// Get numstat for modified files (working tree vs HEAD)
// Get numstat for modified tracked files (working tree vs HEAD)
numstatOut, err := run("git", "diff", "--numstat", "HEAD")
if err == nil && strings.TrimSpace(numstatOut) != "" {
lines := strings.Split(strings.TrimSpace(numstatOut), "\n")
Expand All @@ -896,11 +898,37 @@ func DiffRepo(ctx context.Context, repoDir string) (*DiffSummary, error) {
fmt.Sscanf(removed, "%d", &n)
summary.TotalRemoved += n
}
// If file was deleted (0 added, all removed), count as removed file
if added == "0" && removed != "0" {
summary.FilesRemoved++
}
}
}

// Get untracked files (new files not yet added to git)
untrackedOut, err := run("git", "ls-files", "--others", "--exclude-standard")
if err == nil && strings.TrimSpace(untrackedOut) != "" {
untrackedFiles := strings.Split(strings.TrimSpace(untrackedOut), "\n")
for _, filePath := range untrackedFiles {
if filePath == "" {
continue
}
// Count lines in the untracked file
fullPath := filepath.Join(repoDir, filePath)
if data, err := os.ReadFile(fullPath); err == nil {
// Count lines (all lines in a new file are "added")
lineCount := strings.Count(string(data), "\n")
if len(data) > 0 && !strings.HasSuffix(string(data), "\n") {
lineCount++ // Count last line if it doesn't end with newline
}
summary.TotalAdded += lineCount
summary.FilesAdded++
}
}
}

log.Printf("gitDiffRepo: total_added=%d total_removed=%d",
summary.TotalAdded, summary.TotalRemoved)
log.Printf("gitDiffRepo: files_added=%d files_removed=%d total_added=%d total_removed=%d",
summary.FilesAdded, summary.FilesRemoved, summary.TotalAdded, summary.TotalRemoved)
return summary, nil
}

Expand Down
40 changes: 39 additions & 1 deletion components/backend/handlers/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,22 @@ func ContentGitDiff(c *gin.Context) {

summary, err := GitDiffRepo(c.Request.Context(), repoDir)
if err != nil {
c.JSON(http.StatusOK, gin.H{"total_added": 0, "total_removed": 0})
c.JSON(http.StatusOK, gin.H{
"files": gin.H{
"added": 0,
"removed": 0,
},
"total_added": 0,
"total_removed": 0,
})
return
}

c.JSON(http.StatusOK, gin.H{
"files": gin.H{
"added": summary.FilesAdded,
"removed": summary.FilesRemoved,
},
"total_added": summary.TotalAdded,
"total_removed": summary.TotalRemoved,
})
Expand All @@ -146,23 +157,31 @@ func ContentWrite(c *gin.Context) {
Encoding string `json:"encoding"`
}
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("ContentWrite: bind JSON failed: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
log.Printf("ContentWrite: path=%q contentLen=%d encoding=%q StateBaseDir=%q", req.Path, len(req.Content), req.Encoding, StateBaseDir)

path := filepath.Clean("/" + strings.TrimSpace(req.Path))
if path == "/" || strings.Contains(path, "..") {
log.Printf("ContentWrite: invalid path rejected: path=%q", path)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid path"})
return
}
abs := filepath.Join(StateBaseDir, path)
log.Printf("ContentWrite: absolute path=%q", abs)

if err := os.MkdirAll(filepath.Dir(abs), 0755); err != nil {
log.Printf("ContentWrite: mkdir failed for %q: %v", filepath.Dir(abs), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create directory"})
return
}
var data []byte
if strings.EqualFold(req.Encoding, "base64") {
b, err := base64.StdEncoding.DecodeString(req.Content)
if err != nil {
log.Printf("ContentWrite: base64 decode failed: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid base64 content"})
return
}
Expand All @@ -171,42 +190,60 @@ func ContentWrite(c *gin.Context) {
data = []byte(req.Content)
}
if err := os.WriteFile(abs, data, 0644); err != nil {
log.Printf("ContentWrite: write failed for %q: %v", abs, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to write file"})
return
}
log.Printf("ContentWrite: successfully wrote %d bytes to %q", len(data), abs)
c.JSON(http.StatusOK, gin.H{"message": "ok"})
}

// ContentRead handles GET /content/file?path=
func ContentRead(c *gin.Context) {
path := filepath.Clean("/" + strings.TrimSpace(c.Query("path")))
log.Printf("ContentRead: requested path=%q StateBaseDir=%q", c.Query("path"), StateBaseDir)
log.Printf("ContentRead: cleaned path=%q", path)

if path == "/" || strings.Contains(path, "..") {
log.Printf("ContentRead: invalid path rejected: path=%q", path)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid path"})
return
}
abs := filepath.Join(StateBaseDir, path)
log.Printf("ContentRead: absolute path=%q", abs)

b, err := os.ReadFile(abs)
if err != nil {
log.Printf("ContentRead: read failed for %q: %v", abs, err)
if os.IsNotExist(err) {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": "read failed"})
}
return
}
log.Printf("ContentRead: successfully read %d bytes from %q", len(b), abs)
c.Data(http.StatusOK, "application/octet-stream", b)
}

// ContentList handles GET /content/list?path=
func ContentList(c *gin.Context) {
path := filepath.Clean("/" + strings.TrimSpace(c.Query("path")))
log.Printf("ContentList: requested path=%q", c.Query("path"))
log.Printf("ContentList: cleaned path=%q", path)
log.Printf("ContentList: StateBaseDir=%q", StateBaseDir)

if path == "/" || strings.Contains(path, "..") {
log.Printf("ContentList: invalid path rejected: path=%q", path)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid path"})
return
}
abs := filepath.Join(StateBaseDir, path)
log.Printf("ContentList: absolute path=%q", abs)

info, err := os.Stat(abs)
if err != nil {
log.Printf("ContentList: stat failed for %q: %v", abs, err)
if os.IsNotExist(err) {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
} else {
Expand Down Expand Up @@ -241,5 +278,6 @@ func ContentList(c *gin.Context) {
"modifiedAt": info.ModTime().UTC().Format(time.RFC3339),
})
}
log.Printf("ContentList: returning %d items for path=%q", len(items), path)
c.JSON(http.StatusOK, gin.H{"items": items})
}
Loading
Loading