Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
99020c2
fix: handle worktree deletion when directory is already removed
linletian Mar 11, 2026
4ab0627
Merge pull request #8 from linletian/fix/deleted-worktree-sync
linletian Mar 11, 2026
cda354e
Add AGENTS.md developer guide and auto-focus terminal feature
linletian Mar 11, 2026
9a92ecb
fix(ui): improve TUI display when switching running instances
linletian Mar 11, 2026
57e68e3
Merge pull request #10 from linletian/fix/web-console-renew
linletian Mar 11, 2026
4cad0b9
Merge branch 'feature/auto-focus-instance-cursor' into develop
linletian Mar 11, 2026
37bb3eb
feat(instance): pass terminal window size from Web UI to PTY
linletian Mar 11, 2026
68afeee
test(instance): add unit tests for Resize method
linletian Mar 11, 2026
2fce7d2
test(instance): use bash instead of zsh for cross-platform compatibility
linletian Mar 11, 2026
1496293
Merge pull request #11 from linletian/feature/passing-window-size
linletian Mar 11, 2026
c830b9f
feat(instance): add WebSocket TTY handshake protocol
linletian Mar 11, 2026
befa6cc
Merge pull request #12 from linletian/fix/refresh-prob-1
linletian Mar 11, 2026
19b39e7
feat(ui): increase terminal scrollback buffer and document client-sid…
linletian Mar 11, 2026
8b56ce2
Merge origin/develop into fix/wheel-mapping-prob and resolve conflicts
linletian Mar 11, 2026
6c0d059
Merge pull request #13 from linletian/fix/wheel-mapping-prob
linletian Mar 11, 2026
9b6c9c3
fix: handle terminal control characters and sanitize TUI output
linletian Mar 11, 2026
a1e7aa9
Merge pull request #14 from linletian/fix/top-cli-exited-abnormal-char
linletian Mar 11, 2026
3a14f77
fix: improve terminal control handling and sanitize TUI output
linletian Mar 12, 2026
287db71
fix: match mouse events without leading bracket
linletian Mar 12, 2026
6b51dae
Merge pull request #15 from linletian/fix/top-cli-exited-abnormal-char
linletian Mar 12, 2026
3d1d7c0
fix(ws): fix handshake protocol with proper cancel handling and dual …
linletian Mar 12, 2026
bf2db30
docs: add comprehensive terminal I/O analysis and test cases
linletian Mar 12, 2026
9a7fa56
Merge pull request #16 from linletian/fix/top-cli-exited-abnormal-char
linletian Mar 12, 2026
39a30fa
feat: complete terminal state machine with DISCONNECTING state
linletian Mar 12, 2026
77670a3
fix: prevent terminal query response leakage with state machine and f…
linletian Mar 13, 2026
2d8db44
Merge pull request #17 from linletian/fix/top-cli-exited-abnormal-char
linletian Mar 13, 2026
31d98a6
Merge branch 'develop' into fix/refresh-prob-1
linletian Mar 13, 2026
fb38d15
fix: resolve terminal display and query response issues after develop…
linletian Mar 13, 2026
abbacb7
fix(tests): update redact tests to match disabled backend filtering
linletian Mar 13, 2026
d30025f
Merge pull request #18 from linletian/fix/refresh-prob-1
linletian Mar 13, 2026
4f2188d
fix(ui): refactor terminal output handling for UTF-8 and SSE consistency
linletian Mar 13, 2026
ec8b75f
Merge pull request #19 from linletian/fix/refresh-prob-1
linletian Mar 13, 2026
c3add14
docs: add missing scrollback documentation from PR13
linletian Mar 14, 2026
1dca9fc
fix(ui): disable mouse tracking on instance switch
linletian Mar 14, 2026
5d0292c
docs: update architecture and test cases for instance switch
linletian Mar 14, 2026
bdda603
Merge pull request #20 from linletian/fix/refresh-prob-1
linletian Mar 14, 2026
326be58
docs: prepare v0.1.1 release
linletian Mar 14, 2026
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
269 changes: 269 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# AGENTS.md - Guide for Coding Agents

<!-- gitnexus:start -->
# GitNexus MCP

Expand All @@ -23,3 +25,270 @@ This project is indexed by GitNexus as **myworktree** (221 symbols, 611 relation
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |

<!-- gitnexus:end -->

## Project Overview

myworktree is a lightweight single-user manager for **git worktrees** and **long-running CLI instances**, with a minimal Web UI and HTTP API. It provides isolated workspaces for multiple coding tasks with persistent, re-attachable terminals.

**Key Requirements:**
- Go 1.25+ (see go.mod)
- No external Go dependencies
- Target: macOS 12+ (other platforms not validated)
- Requires: `git`, `zsh`, `script`

## Build, Test, and Lint Commands

### Building
```bash
# Build primary binary
go build -o myworktree ./cmd/myworktree

# Build alias command (auto-opens browser by default)
go build -o mw ./cmd/mw

# Run server (MUST be inside target git repo)
go run ./cmd/myworktree -listen 127.0.0.1:0
```

### Testing
```bash
# Run full test suite
go test ./...

# Run tests for a specific package with verbose output
go test ./internal/worktree -v

# Run a single test function
go test ./internal/app -run TestNormalizeLabels -v

# Run integration tests (may require git setup)
go test ./internal/worktree -run TestIntegration -v
```

### Linting and Formatting
```bash
# Format check used by CI (must pass before commit)
test -z "$(gofmt -l .)"

# Auto-format all Go files
gofmt -w .

# Verify formatting
gofmt -d .
```

### Pre-commit Checklist
```bash
test -z "$(gofmt -l .)" && go test ./... && go build -o myworktree ./cmd/myworktree && go build -o mw ./cmd/mw
```

## Code Style Guidelines

### Formatting
- **Use gofmt**: All code must be formatted with `gofmt`. This is enforced by CI.
- **No external formatters**: Do not introduce additional formatting tools.

### Imports
Group imports in this order with blank lines between:
1. Standard library packages
2. Third-party packages (none in this project)
3. Internal packages (prefixed with `myworktree/internal/`)

```go
import (
"errors"
"fmt"
"os"

"myworktree/internal/store"
"myworktree/internal/worktree"
)
```

### Naming Conventions
- **Exported names**: PascalCase (e.g., `ManagedWorktree`, `FileStore`)
- **Unexported names**: camelCase (e.g., `dataDir`, `worktreeMgr`)
- **Acronyms**: Keep uppercase (e.g., `ID`, `PID`, `HTTP`, `JSON`)
- **Interfaces**: Use `-er` suffix for single-method interfaces (e.g., `Manager`, `Store`)
- **Constants**: MixedCaps or ALL_CAPS for exported constants
- **Error variables**: Prefix with `err` (e.g., `errInvalidRepoListenPort`)

### Type Definitions
- **Structs**: Define with JSON tags for serializable types
- **Prefer composition**: Embed structs rather than inheritance
- **Constructor pattern**: Use `New()` functions that return (`*Type, error`)

```go
type Config struct {
ListenAddr string
AuthToken string
WorktreesDir string
}

type Manager struct {
GitRoot string
DataDir string
WorktreesDir string
Store store.FileStore
}

func New(cfg Config, logger *log.Logger) (*Server, error) {
// Validate inputs
if logger == nil {
return nil, errors.New("logger is required")
}
// ... initialization
return &Server{...}, nil
}
```

### Error Handling
- **Return errors explicitly**: Do not panic in library code
- **Wrap errors**: Provide context when appropriate
- **Use errors.New()** for simple static error messages
- **Define package-level error variables** for errors that need comparison

```go
var errInvalidRepoListenPort = errors.New("invalid persisted listen_port")

func (m Manager) Create(taskDesc string) (store.ManagedWorktree, error) {
if strings.TrimSpace(taskDesc) == "" {
return store.ManagedWorktree{}, errors.New("task description is required")
}
// ... implementation
}
```

### Resource Management
- **Use defer for cleanup**: Always defer Close(), Unlock(), etc.
- **Check errors in defer**: Use blank identifier for cleanup errors when appropriate

```go
f, err := os.OpenFile(path, os.O_RDONLY, 0o600)
if err != nil {
return err
}
defer f.Close()

if err := syscall.Flock(int(f.Fd()), syscall.LOCK_SH); err != nil {
return err
}
defer func() { _ = syscall.Flock(int(f.Fd(), syscall.LOCK_UN) }()
```

### Concurrency
- **Protect shared state**: Use `sync.Mutex` or `sync.RWMutex` for thread safety
- **Lock at the right granularity**: Hold locks for minimal time
- **Document thread safety**: Comment on whether functions are thread-safe

```go
type Manager struct {
stateMu sync.Mutex // protects Store access
mu sync.Mutex // protects running/inputs maps
// ...
}

func (m *Manager) Start(in StartInput) (store.ManagedInstance, error) {
m.mu.Lock()
if m.running == nil {
m.running = map[string]*exec.Cmd{}
}
m.mu.Unlock()
// ... rest of implementation
}
```

### Testing Conventions
- **Test file naming**: `<name>_test.go` in the same package
- **Test function naming**: `Test<FunctionName>` or `Test<Scenario>`
- **Table-driven tests**: Prefer for multiple test cases
- **Integration tests**: Suffix with `_integration_test.go` or use build tags

```go
func TestSlugify(t *testing.T) {
got := slugify("Fix login 401 & add tests!")
want := "fix-login-401-add-tests"
if got != want {
t.Fatalf("unexpected slugify result: got %q, want %q", got, want)
}
}
```

### Comments and Documentation
- **Package comments**: Start with "Package <name>" for package docs
- **Exported types/functions**: Add doc comments explaining purpose
- **Implementation comments**: Explain "why", not "what"
- **No TODO/FIXME comments** in production code without tracking issue

### File Organization
- **One primary type per file**: Named after the type (e.g., `manager.go` for `Manager`)
- **Test files**: Same directory as source with `_test.go` suffix
- **Package structure**: Follow internal package layout:
- `app/` - HTTP server, routing, auth
- `worktree/` - Worktree lifecycle management
- `instance/` - Process management
- `store/` - State persistence
- `tag/` - Tag configuration
- `redact/` - Log redaction
- `ui/` - Embedded static UI
- `mcp/` - MCP adapter
- `gitx/` - Git operations
- `ws/` - WebSocket handling
- `cli/` - CLI command handling
- `version/` - Version information

### JSON and Serialization
- **Use struct tags**: Always include `json` tags for serializable fields
- **Omit empty fields**: Use `omitempty` for optional fields
- **Use consistent field names**: Prefer snake_case in JSON for API compatibility

```go
type ManagedWorktree struct {
ID string `json:"id"`
Name string `json:"name"`
Path string `json:"path"`
Branch string `json:"branch"`
CreatedAt string `json:"created_at"`
}
```

## Important Architectural Notes

1. **Run Location Matters**: The server MUST be executed inside the target git repository because repo detection and per-project data directories derive from CWD.

2. **State Storage**: All persisted state uses `store.FileStore` with file locking and atomic writes. Never write state directly; always go through the store.

3. **Branch Naming**:
- Default: `mwt/<slug>`
- Custom: If task description is `<group>/<name>`, branch is `<group>/<name>` (no prefix)
- Slug is ASCII-only, max 48 chars, falls back to "worktree" if empty

4. **Strict Deletion**: Worktree deletion refuses if `git status --porcelain` is non-empty (includes untracked files).

5. **Security Defaults**:
- Server defaults to loopback listen
- Non-loopback requires `--auth` token
- Optional TLS via `--tls-cert/--tls-key`
- Log redaction for secrets (e.g., `sk-...`)
- Env sanitization for TOKEN/SECRET/KEY/PASSWORD keys

## CI/CD

GitHub Actions runs on:
- Pushes to `develop` and `main` branches
- Pull requests targeting `develop` and `main`

CI Pipeline (`.github/workflows/go-ci.yml`):
1. Verify `gofmt` formatting
2. Run `go test ./...`
3. Build both binaries (`myworktree` and `mw`)

Release Process (`.github/workflows/release.yml`):
- Triggered by `v*` tags
- Builds darwin `amd64`/`arm64` archives with checksums

## Additional Resources

- **Documentation**: See `docs/` directory for PRD, Architecture, and API docs
- **Changelog**: See `CHANGELOG.md` for version history
- **License**: MIT License (see `LICENSE` file)
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## v0.1.1

Recommended stable release after the `v0.1.0` GitHub Release assets were withdrawn during post-release validation. The `v0.1.0` tag remains the comparison baseline, but `v0.1.1` is the supported public release.

Highlights:
- Fixed multiple terminal/TUI regressions that could leak terminal query responses into the shell, break UTF-8 output across transport boundaries, or leave the terminal in a bad state after switching instances.
- Added a WebSocket TTY handshake plus browser-to-PTY resize propagation so interactive programs start with a valid terminal size and reconnect more reliably.
- Improved terminal UX with larger client-side scrollback, safer reconnect/state transitions, and instance-switch behavior that avoids stale mouse-tracking side effects.
- Fixed managed worktree edge cases when deleting entries whose directories were already removed, and kept compatibility when importing older `wt/<name>` worktrees.

Documentation and validation:
- Added deeper terminal I/O analysis, filter review notes, and terminal-focused test cases to document the root causes behind the `v0.1.1` fixes.
- Expanded automated coverage for PTY resize behavior, redaction behavior, and worktree integration scenarios touched by the release.

## v0.1.0

Initial public release of `myworktree`.
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ Example:

```bash
# Pick the archive that matches your Mac, then verify and unpack it.
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.0/myworktree_v0.1.0_darwin_arm64.tar.gz
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.0/checksums.txt
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.1/myworktree_v0.1.1_darwin_arm64.tar.gz
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.1/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
tar -xzf myworktree_v0.1.0_darwin_arm64.tar.gz
tar -xzf myworktree_v0.1.1_darwin_arm64.tar.gz

# Optional: install into PATH
sudo install -m 755 ./mw /usr/local/bin/mw
Expand All @@ -71,6 +71,8 @@ sudo install -m 755 ./myworktree /usr/local/bin/myworktree
mw --version
```

Start from `v0.1.1` or newer for public release binaries. The earlier `v0.1.0` GitHub Release assets were withdrawn after post-release validation uncovered severe terminal interaction issues.

Each release archive contains `mw`, `myworktree`, `README.md`, `LICENSE`, and `CHANGELOG.md`.
If there is no prerelease/release asset yet, or you need a platform we do not publish, follow the source build steps below.

Expand Down
10 changes: 7 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ myworktree 只做管理,不碰项目具体内容:
## 功能(MVP)
- 受管 worktree:创建/列表/纳入管理(import)/删除(严格删除:dirty 则拒绝)
- 受管 instance:基于 Tag 启动模板启动/停止/重启/列表
- instance 重启会保留 worktree、tag/命令、labels,并串联旧/新实例记录
- 支持可选 instance labels(`k=v`),可用于 UI 过滤与搜索
- 默认 WebSocket Web TTY 交互(并保留 SSE/HTTP 兜底)
- 前端页面关闭/刷新后:后端 instance 继续运行;重新打开可回放输出并继续交互
- UI 提供传输状态标记(websocket/sse/polling)和 WS 重连按钮
Expand Down Expand Up @@ -56,10 +58,10 @@ myworktree 只做管理,不碰项目具体内容:

```bash
# 根据你的 Mac 机型选择对应压缩包,然后校验并解压
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.0/myworktree_v0.1.0_darwin_arm64.tar.gz
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.0/checksums.txt
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.1/myworktree_v0.1.1_darwin_arm64.tar.gz
curl -LO https://github.com/linletian/myworktree/releases/download/v0.1.1/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
tar -xzf myworktree_v0.1.0_darwin_arm64.tar.gz
tar -xzf myworktree_v0.1.1_darwin_arm64.tar.gz

# 可选:安装到 PATH
sudo install -m 755 ./mw /usr/local/bin/mw
Expand All @@ -69,6 +71,8 @@ sudo install -m 755 ./myworktree /usr/local/bin/myworktree
mw --version
```

建议从 `v0.1.1` 或更新版本开始使用公开发布版二进制。更早的 `v0.1.0` GitHub Release 资产在补充实测中发现严重终端交互问题后已撤回。

每个发布压缩包内都包含 `mw`、`myworktree`、`README.md`、`LICENSE` 和 `CHANGELOG.md`。
如果当前还没有预发布/正式发布压缩包,或者你的平台暂无对应产物,就直接使用下面的源码编译步骤。

Expand Down
Loading