Thanks to visit codestin.com
Credit goes to github.com

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
1 change: 0 additions & 1 deletion staged/.npmrc

This file was deleted.

87 changes: 8 additions & 79 deletions staged/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,20 @@
# AGENTS.md

## Overview

**Staged** is a desktop git diff viewer. Tauri app with Rust backend (libgit2) and Svelte/TypeScript frontend.

## Architecture

Frontend calls Rust via Tauri's `invoke()`. All git operations happen in Rust using libgit2.

### Core Concepts

- **DiffId**: A diff is identified by `base..head` refs (e.g., `main..HEAD`, `HEAD..@`)
- **`@` = working tree**: Special ref representing uncommitted changes on disk
- **Review sessions**: Stored per DiffId, contain comments, edits, and reviewed file markers

### Backend Structure
## Commands

```
src-tauri/src/
├── diff/ # Core module
│ ├── git.rs # Git operations: compute_diff, get_refs, resolve_ref
│ ├── types.rs # FileDiff, LinePair, Alignment, GitRef, etc.
│ ├── actions.rs # stage_file, unstage_file, discard_file, etc.
│ ├── review.rs # ReviewStore, Review, Comment, Edit persistence
│ └── watcher.rs # File system watcher for auto-refresh
├── lib.rs # Tauri command handlers (thin layer over diff module)
└── refresh.rs # Debounced refresh controller
```
use `just check-all` before you finalize any commit.
generally don't run the dev server unless asked, usually it is run from a UI integration.

### Frontend Structure
## Backend

```
src/
├── App.svelte # Main shell, state management
└── lib/
├── Sidebar.svelte # File list with review status
├── DiffViewer.svelte # Side-by-side diff with syntax highlighting
├── DiffSelectorModal.svelte # Ref autocomplete picker
├── types.ts # TypeScript types mirroring Rust
├── theme.ts # Color definitions
└── services/
├── git.ts # Tauri invoke wrappers
├── review.ts # Review session API
├── highlighter.ts # Syntax highlighting (Shiki)
├── scrollSync.ts # Synchronized scroll for diff panels
└── statusEvents.ts # File watcher event handling
```
We are intentionally conservative with our data models. **Before adding fields or new types to
the backend, get human review.**

### Design Principles
Generally we want to avoid reconciliation of state, so git is authoritative for anything it tracks.

- **Stateless backend**: Git is the source of truth. Rust functions discover repo state fresh each call—no cached state that can drift.
- **Rebuildable features**: Self-contained modules with clear boundaries. Features can be deleted and rebuilt without surgery across the codebase.
- **Horizontal space is precious**: Side-by-side diffs need width. Minimize chrome, prefer overlays, hide UI when possible.
## Frontend

### Theming

Colors defined in `src/lib/theme.ts`, applied via CSS custom properties in `app.css`.
All components use `var(--*)` for colors—no hardcoded values.

## Commands

```bash
just install # First-time setup (deps + git hooks)
just dev # Run with hot-reload (human runs this)
just build # Production build
just fmt # Auto-format all code
just lint # Clippy for Rust
just typecheck # Type-check frontend + backend
just check-all # Format + lint + typecheck (run before pushing)
just ci # Same checks without modifying files (for CI)
just clean # Nuke all build artifacts and dependencies
```

**Note:** The human runs the dev server. Don't start it yourself.

## Code Quality

Before finishing work:
```bash
just check-all # Auto-formats, then verifies lint + types pass
```

## Git Workflow

**Do not** create branches, commit, or push unless explicitly asked.

**Before pushing PRs**, always run:
```bash
just check-all # Format, lint, and typecheck
```
186 changes: 186 additions & 0 deletions staged/docs/workdir-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Workdir Design: Separating Branches from Working Directories

## Problem

Staged currently hard-couples branches to worktrees: every `Branch` has a
`worktree_path: TEXT NOT NULL`, and creating a branch always means
`git worktree add`. This works well for normal-sized repos but falls apart for
large monorepos where each worktree costs gigabytes of disk and minutes of
setup time (dependency installs, build caches, etc.).

We want to support parallel agent work as the default, but also support users
who can't afford N full working copies on disk.

## Core Idea

Separate **what you're working on** (a branch) from **where the work happens**
(a working directory). A working directory is a pooled resource owned by the
project. Branches are assigned to a workdir when they need one and release it
when they don't.

```
Project
├── workdirs[] pool of filesystem locations
└── branches[] logical branches, assigned to a workdir when active
```

## Target Data Model

### Workdir (new table)

A filesystem location where git operations can happen. Could be the main
checkout itself, or a `git worktree`.

```sql
CREATE TABLE workdirs (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
path TEXT NOT NULL,
branch_id TEXT REFERENCES branches(id) ON DELETE SET NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(project_id, path)
);
```

`branch_id` is nullable — a workdir with no branch is **available**. A workdir
with a branch is **occupied**. The UNIQUE constraint on `(project_id, path)`
prevents duplicates. A separate unique constraint or application-level check
ensures at most one workdir points to a given `branch_id`.

### Branch (changed)

Drop `worktree_path`. A branch's working directory is found by joining through
`workdirs` — or it may not have one at all (branch exists in git but isn't
checked out anywhere).

```sql
CREATE TABLE branches (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
branch_name TEXT NOT NULL,
base_branch TEXT NOT NULL,
pr_number INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(project_id, branch_name)
);
```

### How Modes Emerge

No strategy enum needed. The mode is determined by how many workdirs a project
has and how they're provisioned:

| Mode | Workdirs | Parallelism | How it works |
|------|----------|-------------|--------------|
| **Full** (current default) | 1 per branch | Unlimited | Creating a branch also creates a workdir via `git worktree add`. 1:1 lifetime. |
| **Shared** (monorepo) | 1 total (the repo itself) | Serial | All branches share the repo checkout. Activating a branch does `git checkout` in that workdir. Only one branch active at a time. |
| **Pool** (future) | Fixed N (e.g. 3-4) | Up to N | User pre-creates a few persistent worktrees. Branches claim an available workdir when they need one, release when done. |

### Key Query: "Where do I run git for this branch?"

```sql
SELECT w.path
FROM workdirs w
WHERE w.branch_id = ?
```

Returns one row if the branch is checked out somewhere, zero rows if it isn't.
All downstream code that currently reads `branch.worktree_path` would use this
instead.

### Key Query: "Can I start work on this branch?"

```sql
-- Find an available workdir for this project
SELECT w.id, w.path
FROM workdirs w
WHERE w.project_id = ? AND w.branch_id IS NULL
LIMIT 1
```

If a row comes back, assign the branch to it (checkout + update `branch_id`).
If no row comes back, the branch queues — the UI can show "waiting for a
workdir" and the scheduler retries when one frees up.

## What to Do Now (Before Implementing Shared/Pool)

The current model only needs to support "full" mode today. The question is:
what do we change *now* so that adding workdirs later is easy and doesn't
require a painful migration?

### Recommended: introduce `workdirs` now, keep it 1:1

Add the `workdirs` table. When creating a branch with `git worktree add`,
also create a workdir row and link them. Branch keeps a `workdir_id` for
convenience (nullable FK), but the workdir table is the source of truth for
the path.

**Branch model becomes:**

```rust
pub struct Branch {
pub id: String,
pub project_id: String,
pub branch_name: String,
pub base_branch: String,
pub pr_number: Option<u64>,
pub created_at: i64,
pub updated_at: i64,
}
```

No `worktree_path`. Code that needs the path does:

```rust
let workdir = store.get_workdir_for_branch(&branch.id)?;
let path = workdir.map(|w| w.path);
```

**Workdir model:**

```rust
pub struct Workdir {
pub id: String,
pub project_id: String,
pub path: String,
pub branch_id: Option<String>,
pub created_at: i64,
pub updated_at: i64,
}
```

### Why not just keep `worktree_path` on Branch for now?

It's tempting to defer, but `worktree_path: TEXT NOT NULL` bakes in two
assumptions that are wrong for shared/pool modes:

1. **Every branch always has a path** — in shared/pool modes, most branches
won't be checked out anywhere. Making this nullable is a half-measure that
leaves the field on the wrong table.

2. **The path is a static property of the branch** — in pool mode the path
changes every time the branch gets assigned to a different workdir. Storing
it on the branch means updating it on every swap, with the workdir table
as the actual source of truth anyway.

Introducing the workdir table now means:

- The Branch struct and schema are clean — no field that's "sometimes null,
sometimes stale, will be moved later."
- All code that touches working directory paths goes through one pattern from
the start, so there's no scattered `branch.worktree_path` usage to hunt
down later.
- Adding shared/pool modes is purely additive: change how workdirs are
provisioned, add scheduling logic. No schema migration, no model refactor.

### What about the main repo checkout?

When a project is created in "full" mode, the main repo checkout at
`project.repo_path` is *not* tracked as a workdir. It's just where we run
`git worktree add` from. Workdirs are only the locations we manage.

When we later add "shared" mode, the repo path itself *becomes* the single
workdir. That's when we create a workdir row pointing to `project.repo_path`.
This is a clean additive change — no existing rows need updating.
11 changes: 2 additions & 9 deletions staged/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>staged</title>
<!-- Prevent white flash by setting dark background before CSS loads -->
<style>
html, body, #app {
background-color: #1e1e1e;
margin: 0;
padding: 0;
min-height: 100vh;
}
</style>
<!-- The window starts hidden and is shown by App.svelte after the
theme is applied, so no inline style hacks are needed here. -->
</head>
<body>
<div id="app"></div>
Expand Down
Loading
Loading