Add unit test to verify KUGETSU_TEMP_DIR is exported in cmd_delegate. Also update SKILL.md to document KUGETSU_TEMP_DIR config option.
393 lines
12 KiB
Markdown
393 lines
12 KiB
Markdown
---
|
|
name: kugetsu
|
|
description: Issue-driven session manager for opencode CLI. Manages base sessions and per-issue forked sessions with automatic indexing for headless orchestration.
|
|
license: MIT
|
|
compatibility: Requires opencode CLI, bash, python3, and filesystem access.
|
|
metadata:
|
|
author: shoko
|
|
version: "2.2"
|
|
---
|
|
|
|
# kugetsu - OpenCode Session Manager (Issue-Driven)
|
|
|
|
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
|
|
|
|
### For Human Users
|
|
Run once on a new host:
|
|
```bash
|
|
. skills/kugetsu/scripts/kugetsu-install.sh
|
|
```
|
|
|
|
### For Agents (Self-Install)
|
|
Copy the script to your PATH:
|
|
```bash
|
|
cp skills/kugetsu/scripts/kugetsu ~/.local/bin/kugetsu
|
|
chmod +x ~/.local/bin/kugetsu
|
|
```
|
|
|
|
## Configuration
|
|
|
|
User overrides can be set in `~/.kugetsu/config`. This file is sourced on each kugetsu command call, so changes take effect immediately without re-initialization.
|
|
|
|
A default config file is created during `kugetsu init` with commented examples:
|
|
|
|
```bash
|
|
# User configuration overrides
|
|
# Values set here take precedence over defaults
|
|
# Changes take effect immediately (no re-init needed)
|
|
|
|
# Max concurrent dev agents (default: 3)
|
|
# MAX_CONCURRENT_AGENTS=5
|
|
```
|
|
|
|
### Available Config Options
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `MAX_CONCURRENT_AGENTS` | 3 | Maximum number of concurrent dev agents |
|
|
| `KUGETSU_TEMP_DIR` | `~/.local/share/opencode/tool-output` | Temp directory for subagent tool output (useful in headless environments where /tmp is restricted) |
|
|
|
|
### Environment Variables for Agents
|
|
|
|
Agents receive environment variables through env files, not command-line injection. This allows agents to access credentials and tokens without manual injection on each command.
|
|
|
|
**Files created during `kugetsu init`:**
|
|
- `~/.kugetsu/env/default.env` - Variables for all agents
|
|
- `~/.kugetsu/env/pm-agent.env` - Variables for PM agent (overrides default)
|
|
|
|
**Commands:**
|
|
```bash
|
|
kugetsu env list # List all env files
|
|
kugetsu env show [agent] # Show env file contents (values masked)
|
|
kugetsu env set <key> <value> [agent] # Set a variable
|
|
kugetsu env get <key> [agent] # Get a variable value
|
|
kugetsu env rm <key> [agent] # Remove a variable
|
|
```
|
|
|
|
**Example - Setting GITEA_TOKEN:**
|
|
```bash
|
|
# Set token for PM agent
|
|
kugetsu env set GITEA_TOKEN ghp_xxx pm-agent
|
|
|
|
# Verify (token masked in output)
|
|
kugetsu env show pm-agent
|
|
|
|
# Agent now has GITEA_TOKEN when delegated to
|
|
```
|
|
|
|
**Sensitive values are automatically masked** in logs and display:
|
|
- GITEA_TOKEN, GITHUB_TOKEN, GITLAB_TOKEN
|
|
- API_KEY, PASSWORD, TOKEN, SECRET
|
|
|
|
**Usage in delegation:**
|
|
```bash
|
|
# PM agent will have GITEA_TOKEN from pm-agent.env
|
|
kugetsu delegate "post comment on #69"
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Session Pattern
|
|
- **Base Session**: Created once via TUI, used for forking dev agents
|
|
- **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/
|
|
├── sessions/
|
|
│ ├── 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
|
|
```
|
|
|
|
### Index File
|
|
```json
|
|
{
|
|
"base": "ses_abc123",
|
|
"pm_agent": "ses_pm_xyz789",
|
|
"issues": {
|
|
"github.com/shoko/kugetsu#14": "github.com-shoko-kugetsu-14.json"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Session File
|
|
```json
|
|
{
|
|
"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"
|
|
}
|
|
```
|
|
|
|
## Issue Ref Format
|
|
|
|
All issue references use the format: `instance/user/repo#identifier`
|
|
|
|
Examples:
|
|
- `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
|
|
|
|
### kugetsu init [--force]
|
|
|
|
Initialize base + PM agent sessions via TUI:
|
|
```bash
|
|
kugetsu init
|
|
```
|
|
|
|
- Requires a terminal (TTY) to spawn the opencode TUI
|
|
- Creates base session and PM agent session
|
|
- Stores both session IDs in `index.json`
|
|
- Subsequent runs error unless `--force` is used
|
|
|
|
### 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"
|
|
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)
|
|
- Uses `opencode run --fork --session <base-session-id> "<message>" --workdir <worktree>`
|
|
|
|
### 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>" --workdir <worktree>`
|
|
|
|
### kugetsu list
|
|
|
|
List all tracked sessions:
|
|
```bash
|
|
kugetsu list
|
|
```
|
|
|
|
Output:
|
|
```
|
|
ISSUE_REF TYPE SESSION_ID WORKTREE
|
|
────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
(base) base ses_abc123 N/A
|
|
(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 and worktrees:
|
|
```bash
|
|
kugetsu prune # Shows what would be deleted
|
|
kugetsu prune --force # Deletes orphaned items
|
|
```
|
|
|
|
- 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 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
|
|
```
|
|
|
|
### kugetsu destroy --pm-agent [-y]
|
|
|
|
Delete PM agent session (requires explicit `--pm-agent`):
|
|
```bash
|
|
kugetsu destroy --pm-agent -y
|
|
```
|
|
|
|
### kugetsu destroy --base [-y]
|
|
|
|
Delete base session (requires explicit `--base`):
|
|
```bash
|
|
kugetsu destroy --base -y
|
|
```
|
|
|
|
**Note**: Destroying base also destroys PM agent since PM depends on base.
|
|
|
|
## Workflow Example
|
|
|
|
```bash
|
|
# First-time setup (requires TTY)
|
|
kugetsu init
|
|
# Creates: base session + pm-agent session
|
|
|
|
# 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"
|
|
|
|
# Continue again
|
|
kugetsu continue github.com/shoko/kugetsu#14 "fix failing test"
|
|
|
|
# List all sessions
|
|
kugetsu list
|
|
|
|
# Clean up orphaned items
|
|
kugetsu prune --force
|
|
|
|
# Delete session and worktree when done
|
|
kugetsu destroy github.com/shoko/kugetsu#14
|
|
```
|
|
|
|
## Headless Operation
|
|
|
|
This design solves the headless CLI limitation discovered in Issue #14:
|
|
|
|
1. **Problem**: `opencode run --session <new>` doesn't work headlessly (SSE stream terminates)
|
|
2. **Solution**: Fork from existing base session, which works headlessly
|
|
|
|
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 and worktrees
|
|
3. For full reset: `kugetsu destroy --base -y && kugetsu init`
|
|
|
|
## Remote Access via SSH (Optional)
|
|
|
|
To access kugetsu from a remote machine, SSH setup is required.
|
|
|
|
### Automated Setup
|
|
|
|
Run the SSH setup script inside your container:
|
|
|
|
```bash
|
|
chmod +x skills/kugetsu/scripts/sshd-setup.sh
|
|
bash skills/kugetsu/scripts/sshd-setup.sh <username>
|
|
```
|
|
|
|
Omit `<username>` to use default user `kugetsu`.
|
|
|
|
### What It Does
|
|
|
|
- Checks systemd prerequisite
|
|
- Creates non-root user
|
|
- Configures SSH for key-only authentication
|
|
- Enables passwordless sudo for the user
|
|
- Starts sshd via systemd
|
|
|
|
### After Setup
|
|
|
|
1. Add your SSH public key to `~/.ssh/authorized_keys` on the container
|
|
2. Configure port forwarding on the host (see [docs/kugetsu-setup.md](../../docs/kugetsu-setup.md))
|
|
3. Connect: `ssh -p 2222 <username>@<host-ip>`
|
|
|
|
### Remote Usage
|
|
|
|
Once connected via SSH, kugetsu works the same as local:
|
|
|
|
```bash
|
|
kugetsu list
|
|
kugetsu start github.com/shoko/kugetsu#14 "fix bug"
|
|
kugetsu continue github.com/shoko/kugetsu#14
|
|
```
|
|
|
|
### Documentation
|
|
|
|
See [docs/kugetsu-setup.md](../../docs/kugetsu-setup.md) for full remote access setup including host-side port forwarding and firewall configuration.
|
|
|
|
### Tailscale VPN (Alternative)
|
|
|
|
If your host does not have a public IP or you need access across different networks, Tailscale provides a VPN solution.
|
|
|
|
**Benefits:**
|
|
- No public IP required
|
|
- Each container gets its own unique Tailscale IP
|
|
- Access from anywhere via Tailscale network
|
|
- Normal internet access still works
|
|
|
|
**Setup:**
|
|
|
|
```bash
|
|
chmod +x skills/kugetsu/scripts/tailscale-setup.sh
|
|
bash skills/kugetsu/scripts/tailscale-setup.sh <username> <device-name>
|
|
```
|
|
|
|
The script will:
|
|
1. Install Tailscale (supports Debian/Ubuntu, Fedora)
|
|
2. Start the tailscaled daemon
|
|
3. Prompt for AUTHKEY or browser-based login
|
|
4. Configure device name (defaults to current hostname)
|
|
|
|
**After Setup:**
|
|
- From any Tailscale device: `ssh <username>@<device-name>`
|
|
- Works across different networks without port forwarding
|
|
|
|
See [docs/kugetsu-setup.md](../../docs/kugetsu-setup.md) for full Tailscale setup documentation.
|
|
|
|
## Without kugetsu
|
|
|
|
If kugetsu is not available, use opencode directly:
|
|
```bash
|
|
# Create base session (requires TTY)
|
|
opencode
|
|
# Note the session ID from: opencode session list
|
|
|
|
# Fork for issue
|
|
opencode run --fork --session <base-session-id> "task"
|
|
|
|
# Continue
|
|
opencode run --continue --session <forked-session-id> "continue"
|
|
```
|
|
|
|
Tradeoff: No issue mapping, no index, manual session tracking, no worktree isolation. |