feat(kugetsu): add git worktree isolation per session
- Each issue session gets isolated git worktree to prevent workspace conflicts
- Worktree created on 'kugetsu start', removed on 'kugetsu destroy'
- Worktree path: ~/.kugetsu/worktrees/{sanitized-issue-ref}/
- Branch naming: fix/issue-{number} or fix/{identifier}
- Worktree always recreated on start (guaranteed clean state)
- 'kugetsu list' now shows worktree path
- 'kugetsu prune' also cleans orphaned worktrees
- 'kugetsu continue' runs opencode with --workdir pointing to worktree
- Update SKILL.md to v2.2 with worktree documentation
Part of issue #19 Phase 3 implementation
This commit is contained in:
@@ -5,12 +5,12 @@ license: MIT
|
||||
compatibility: Requires opencode CLI, bash, python3, and filesystem access.
|
||||
metadata:
|
||||
author: shoko
|
||||
version: "2.1"
|
||||
version: "2.2"
|
||||
---
|
||||
|
||||
# kugetsu - OpenCode Session Manager (Issue-Driven)
|
||||
|
||||
Manages opencode sessions with a base session + forked session pattern optimized for headless orchestration.
|
||||
Manages opencode sessions with a base session + forked session pattern optimized for headless orchestration. Each issue gets an isolated git worktree to prevent workspace conflicts.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -34,6 +34,12 @@ chmod +x ~/.local/bin/kugetsu
|
||||
- **PM Agent Session**: Created during init, persistent coordinator for task management
|
||||
- **Forked Sessions**: One per issue, branched from base via `opencode run --fork --session <base>`
|
||||
|
||||
### Git Worktree Isolation
|
||||
Each issue session gets its own git worktree to prevent conflicts:
|
||||
- Isolated working directory (no file collisions)
|
||||
- Isolated branch (no checkout conflicts)
|
||||
- Shared `.git` objects (efficient storage)
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
~/.kugetsu/
|
||||
@@ -41,6 +47,9 @@ chmod +x ~/.local/bin/kugetsu
|
||||
│ ├── base.json # Base session metadata
|
||||
│ ├── pm-agent.json # PM agent session metadata
|
||||
│ └── github.com-shoko-kugetsu-14.json # Forked session per issue
|
||||
├── worktrees/
|
||||
│ ├── github.com-shoko-kugetsu-14/ # Isolated workdir for issue #14
|
||||
│ └── github.com-shoko-kugetsu-15/ # Isolated workdir for issue #15
|
||||
└── index.json # Maps session IDs and issue refs to session files
|
||||
```
|
||||
|
||||
@@ -58,8 +67,10 @@ chmod +x ~/.local/bin/kugetsu
|
||||
### Session File
|
||||
```json
|
||||
{
|
||||
"type": "base|forked|pm_agent",
|
||||
"type": "forked",
|
||||
"issue_ref": "github.com/shoko/kugetsu#14",
|
||||
"opencode_session_id": "ses_xyz789",
|
||||
"worktree_path": "/home/user/.kugetsu/worktrees/github.com-shoko-kugetsu-14",
|
||||
"created_at": "2026-03-29T18:16:10+02:00",
|
||||
"state": "idle"
|
||||
}
|
||||
@@ -67,12 +78,33 @@ chmod +x ~/.local/bin/kugetsu
|
||||
|
||||
## Issue Ref Format
|
||||
|
||||
All issue references use the format: `instance/user/repo#number`
|
||||
All issue references use the format: `instance/user/repo#identifier`
|
||||
|
||||
Examples:
|
||||
- `github.com/shoko/kugetsu#14`
|
||||
- `gitlab.com/username/project#42`
|
||||
- `codeberg.org/user/repo#100`
|
||||
- `github.com/shoko/kugetsu#14` (issue number)
|
||||
- `github.com/shoko/kugetsu#-discuss` (discussion, no issue number yet)
|
||||
- `gitlab.com/username/project#42` (issue number)
|
||||
|
||||
## Worktree Behavior
|
||||
|
||||
### On `kugetsu start`
|
||||
1. Derives worktree path from issue ref: `~/.kugetsu/worktrees/{sanitized-ref}/`
|
||||
2. If worktree exists: removes and recreates (guaranteed clean state)
|
||||
3. If worktree doesn't exist: creates fresh
|
||||
4. Clones repo, creates branch `fix/issue-{id}`
|
||||
5. Runs opencode with `--workdir` pointing to worktree
|
||||
|
||||
### On `kugetsu destroy`
|
||||
1. Removes worktree via `git worktree remove`
|
||||
2. Deletes session file and index entry
|
||||
|
||||
### Repo Configuration
|
||||
If the repo URL cannot be derived from the issue ref, add to `~/.kugetsu/repos.json`:
|
||||
```json
|
||||
{
|
||||
"github.com/shoko kugetsu#14": "https://custom.repo.url/owner/repo.git"
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
@@ -93,12 +125,13 @@ kugetsu init
|
||||
Start task for an issue by forking from base session:
|
||||
```bash
|
||||
kugetsu start github.com/shoko/kugetsu#14 "fix authentication bug"
|
||||
kugetsu start github.com/shoko/kugetsu#-discuss "research auth options"
|
||||
```
|
||||
|
||||
- Creates isolated git worktree for the issue
|
||||
- Forks new session from base
|
||||
- Requires PM agent to exist (created by init)
|
||||
- Stores mapping in `index.json`
|
||||
- Uses `opencode run --fork --session <base-session-id> "<message>"`
|
||||
- Uses `opencode run --fork --session <base-session-id> "<message>" --workdir <worktree>`
|
||||
|
||||
### kugetsu continue `<issue-ref>` `<message>` [--debug]
|
||||
|
||||
@@ -108,7 +141,7 @@ kugetsu continue github.com/shoko/kugetsu#14 "add unit tests"
|
||||
```
|
||||
|
||||
- Looks up session file from index
|
||||
- Uses `opencode run --continue --session <opencode-session-id> "<message>"`
|
||||
- Uses `opencode run --continue --session <opencode-session-id> "<message>" --workdir <worktree>`
|
||||
|
||||
### kugetsu list
|
||||
|
||||
@@ -119,28 +152,28 @@ kugetsu list
|
||||
|
||||
Output:
|
||||
```
|
||||
ISSUE_REF TYPE SESSION_ID CREATED
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
ISSUE_REF TYPE SESSION_ID WORKTREE
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
(base) base ses_abc123 N/A
|
||||
(pm-agent) pm_agent ses_pm_xyz789 2026-03-29T18:16:10+02:00
|
||||
github.com/shoko/kugetsu#14 forked ses_xyz789 2026-03-29T18:16:10+02:00
|
||||
(pm-agent) pm_agent ses_pm_xyz789 N/A
|
||||
github.com/shoko/kugetsu#14 forked ses_xyz789 /home/user/.kugetsu/worktrees/github.com-shoko-kugetsu-14
|
||||
```
|
||||
|
||||
### kugetsu prune [--force]
|
||||
|
||||
Remove orphaned sessions (files not in index):
|
||||
Remove orphaned sessions and worktrees:
|
||||
```bash
|
||||
kugetsu prune # Shows what would be deleted
|
||||
kugetsu prune --force # Deletes orphaned sessions
|
||||
kugetsu prune --force # Deletes orphaned items
|
||||
```
|
||||
|
||||
- Orphaned = session files in `sessions/` but not in `index.json`
|
||||
- Orphaned = session files or worktrees not in index
|
||||
- Always keeps `base.json` and `pm-agent.json`
|
||||
- Useful after opencode session cleanup
|
||||
|
||||
### kugetsu destroy `<issue-ref>` [-y]
|
||||
|
||||
Delete session for specific issue:
|
||||
Delete session and worktree for specific issue:
|
||||
```bash
|
||||
kugetsu destroy github.com/shoko/kugetsu#14 # Prompts for confirmation
|
||||
kugetsu destroy github.com/shoko/kugetsu#14 -y # Skips confirmation
|
||||
@@ -162,73 +195,6 @@ kugetsu destroy --base -y
|
||||
|
||||
**Note**: Destroying base also destroys PM agent since PM depends on base.
|
||||
|
||||
- Requires a terminal (TTY) to spawn the opencode TUI
|
||||
- Creates base session once; subsequent runs error unless `--force` is used
|
||||
- Stores base session ID in `index.json`
|
||||
|
||||
### kugetsu start `<issue-ref>` `<message>` [--debug]
|
||||
|
||||
Start task for an issue by forking from base session:
|
||||
```bash
|
||||
kugetsu start github.com/shoko/kugetsu#14 "fix authentication bug"
|
||||
```
|
||||
|
||||
- Forks new session from base
|
||||
- Stores mapping in `index.json`
|
||||
- Uses `opencode run --fork --session <base-session-id> "<message>"`
|
||||
|
||||
### kugetsu continue `<issue-ref>` `<message>` [--debug]
|
||||
|
||||
Continue work on an existing issue session:
|
||||
```bash
|
||||
kugetsu continue github.com/shoko/kugetsu#14 "add unit tests"
|
||||
```
|
||||
|
||||
- Looks up session file from index
|
||||
- Uses `opencode run --continue --session <opencode-session-id> "<message>"`
|
||||
|
||||
### kugetsu list
|
||||
|
||||
List all tracked sessions:
|
||||
```bash
|
||||
kugetsu list
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
ISSUE_REF TYPE SESSION_ID CREATED
|
||||
──────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
(base) base ses_abc123 N/A
|
||||
github.com/shoko/kugetsu#14 forked ses_xyz789 2026-03-29T18:16:10+02:00
|
||||
```
|
||||
|
||||
### kugetsu prune [--force]
|
||||
|
||||
Remove orphaned sessions (files not in index):
|
||||
```bash
|
||||
kugetsu prune # Shows what would be deleted
|
||||
kugetsu prune --force # Deletes orphaned sessions
|
||||
```
|
||||
|
||||
- Orphaned = session files in `sessions/` but not in `index.json`
|
||||
- Always keeps `base.json`
|
||||
- Useful after opencode session cleanup
|
||||
|
||||
### kugetsu destroy `<issue-ref>` [-y]
|
||||
|
||||
Delete session for specific issue:
|
||||
```bash
|
||||
kugetsu destroy github.com/shoko/kugetsu#14 # Prompts for confirmation
|
||||
kugetsu destroy github.com/shoko/kugetsu#14 -y # Skips confirmation
|
||||
```
|
||||
|
||||
### kugetsu destroy --base [-y]
|
||||
|
||||
Delete base session (requires explicit `--base`):
|
||||
```bash
|
||||
kugetsu destroy --base -y
|
||||
```
|
||||
|
||||
## Workflow Example
|
||||
|
||||
```bash
|
||||
@@ -238,6 +204,7 @@ kugetsu init
|
||||
|
||||
# Start work on issue
|
||||
kugetsu start github.com/shoko/kugetsu#14 "implement feature X"
|
||||
# Creates: worktree at ~/.kugetsu/worktrees/github.com-shoko-kugetsu-14/
|
||||
|
||||
# Continue later
|
||||
kugetsu continue github.com/shoko/kugetsu#14 "add tests"
|
||||
@@ -248,10 +215,10 @@ kugetsu continue github.com/shoko/kugetsu#14 "fix failing test"
|
||||
# List all sessions
|
||||
kugetsu list
|
||||
|
||||
# Clean up orphaned sessions
|
||||
# Clean up orphaned items
|
||||
kugetsu prune --force
|
||||
|
||||
# Delete session when done
|
||||
# Delete session and worktree when done
|
||||
kugetsu destroy github.com/shoko/kugetsu#14
|
||||
```
|
||||
|
||||
@@ -266,13 +233,14 @@ The pattern:
|
||||
- Base session created once via TUI (interactive)
|
||||
- PM agent session created during init (persistent coordinator)
|
||||
- All subsequent work uses `--fork --session <base>` or `--continue --session <forked>`
|
||||
- Each session works in isolated git worktree
|
||||
|
||||
## Recovery
|
||||
|
||||
If opencode sessions become out of sync:
|
||||
|
||||
1. `kugetsu list` shows tracked sessions
|
||||
2. `kugetsu prune` removes orphaned files
|
||||
2. `kugetsu prune` removes orphaned files and worktrees
|
||||
3. For full reset: `kugetsu destroy --base -y && kugetsu init`
|
||||
|
||||
## Remote Access via SSH (Optional)
|
||||
@@ -362,4 +330,4 @@ opencode run --fork --session <base-session-id> "task"
|
||||
opencode run --continue --session <forked-session-id> "continue"
|
||||
```
|
||||
|
||||
Tradeoff: No issue mapping, no index, manual session tracking.
|
||||
Tradeoff: No issue mapping, no index, manual session tracking, no worktree isolation.
|
||||
Reference in New Issue
Block a user