- count_active_dev_sessions() now excludes pm-agent.json from count - process_queue() now calls kugetsu start directly (not opencode run) - process_queue() uses dynamic batch size = available_slots - process_queue() has retry logic (max 3 attempts) on failure - cmd_start() now uses flock around critical section - Added notification types: task_queued, task_dequeued, task_started, task_completed, task_error - Removed QUEUE_DAEMON_BATCH_SIZE config (no longer needed) Fixes issue #146
16 KiB
name, description, license, compatibility, metadata
| name | description | license | compatibility | metadata | ||||
|---|---|---|---|---|---|---|---|---|
| kugetsu | Issue-driven session manager for opencode CLI. Manages base sessions and per-issue forked sessions with automatic indexing for headless orchestration. | MIT | Requires opencode CLI, bash, python3, and filesystem access. |
|
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:
. skills/kugetsu/scripts/kugetsu-install.sh
For Agents (Self-Install)
Copy the script to your PATH:
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:
# 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) |
KUGETSU_VERBOSITY |
default |
PM agent verbosity level: verbose, default, or quiet |
QUEUE_DAEMON_INTERVAL_MINUTES |
5 | How often daemon polls queue (in minutes) |
QUEUE_CLEANUP_AGE_DAYS |
7 | Auto-cleanup completed/error items older than N days |
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:
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:
# 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:
# 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
.gitobjects (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
├── queue/
│ ├── items/ # Queue item JSON files
│ ├── daemon.pid # Daemon process ID
│ └── daemon.log # Daemon log output
└── index.json # Maps session IDs and issue refs to session files
Index File
{
"base": "ses_abc123",
"pm_agent": "ses_pm_xyz789",
"issues": {
"github.com/shoko/kugetsu#14": "github.com-shoko-kugetsu-14.json"
}
}
Session File
{
"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
- Derives worktree path from issue ref:
~/.kugetsu/worktrees/{sanitized-ref}/ - If worktree exists: removes and recreates (guaranteed clean state)
- If worktree doesn't exist: creates fresh
- Clones repo, creates branch
fix/issue-{id} - Runs opencode with
--workdirpointing to worktree
On kugetsu destroy
- Removes worktree via
git worktree remove - Deletes session file and index entry
Repo Configuration
If the repo URL cannot be derived from the issue ref, add to ~/.kugetsu/repos.json:
{
"github.com/shoko kugetsu#14": "https://custom.repo.url/owner/repo.git"
}
Commands
kugetsu init [--force]
Initialize base + PM agent sessions via TUI:
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
--forceis used
kugetsu start <issue-ref> <message> [--debug]
Start task for an issue by forking from base session:
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:
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:
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:
kugetsu prune # Shows what would be deleted
kugetsu prune --force # Deletes orphaned items
- Orphaned = session files or worktrees not in index
- Always keeps
base.jsonandpm-agent.json - Useful after opencode session cleanup
kugetsu destroy <issue-ref> [-y]
Delete session and worktree for specific issue:
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):
kugetsu destroy --pm-agent -y
kugetsu destroy --base [-y]
Delete base session (requires explicit --base):
kugetsu destroy --base -y
Note: Destroying base also destroys PM agent since PM depends on base.
kugetsu delegate <message>
Send a message to the PM agent for task coordination via queue:
kugetsu delegate "work on issue #14"
kugetsu delegate "review PR #92"
- Always enqueues (fire-and-forget): returns immediately
- Queue daemon polls queue and invokes PM when slots available
- Tasks are processed FIFO (first-in-first-out)
- Use
kugetsu queue listto see pending tasks - Use
kugetsu queue-daemon logsto debug queue processing
kugetsu logs [n]
Show recent delegation logs:
kugetsu logs # Show last 10 logs
kugetsu logs 20 # Show last 20 logs
- Logs are stored in
~/.kugetsu/logs/ - Automatically deletes logs older than 7 days
kugetsu status
Check if kugetsu is properly initialized:
kugetsu status
Output:
kugetsu_not_initialized- No index filebase_session_missing- Base session not foundpm_agent_missing- PM agent not foundok- Everything is initialized
kugetsu doctor [--fix]
Diagnose and fix kugetsu issues:
kugetsu doctor # Show diagnostic info
kugetsu doctor --fix # Attempt automatic repairs
- Checks index file existence
- Validates base and PM agent sessions
- With
--fix: recreates PM agent if missing - With
--fix-permissions: fixes session permissions in opencode database
kugetsu notify [list|clear]
Show or clear notifications from PM agent:
kugetsu notify list # Show unread notifications (default)
kugetsu notify clear # Mark all as read
- PM agent writes task completion notifications to
~/.kugetsu/notifications.json - Shows timestamp, type, message, and issue ref for each notification
kugetsu server <list|add|remove|default|get>
Manage git server configurations:
kugetsu server list # List all configured servers
kugetsu server add github https://github.com # Add a server
kugetsu server remove gitlab # Remove a server
kugetsu server default github # Set default server
kugetsu server get github # Get server URL
kugetsu queue <list|stats|clear>
Manage task queue for autonomous PM operation:
kugetsu queue list # Show queued tasks with status
kugetsu queue stats # Show queue statistics (total, pending, notified, completed, error)
kugetsu queue clear # Clean up old completed/error items
kugetsu queue enqueue <issue-ref> <message> # Manually enqueue a task
Queue Item States:
pending- Waiting in queue, daemon can pick upnotified- PM agent has picked up the taskcompleted- Dev agent finished, PR createderror- Timeout or failure
kugetsu queue-daemon <start|stop|restart|status|logs>
Manage the queue daemon background process:
kugetsu queue-daemon start # Start daemon in background
kugetsu queue-daemon stop # Stop daemon
kugetsu queue-daemon restart # Restart daemon
kugetsu queue-daemon status # Check if daemon is running
kugetsu queue-daemon logs # Show recent daemon logs
Daemon Behavior:
- Runs at configurable interval (default: 5 minutes)
- Checks if active agents < MAX_CONCURRENT_AGENTS
- Picks 1-N pending items (configurable batch size)
- Forks PM session for each picked item
- PM decides whether to use
startorcontinue
Queue Directory:
~/.kugetsu/queue/
├── items/ # Queue item JSON files
│ ├── q_1234567890.json # One file per queued task
│ └── q_1234567891.json
├── daemon.pid # Daemon process ID
├── daemon.lock # Daemon lock file
└── daemon.log # Daemon log output
Workflow Example
First-time Setup
# Initialize kugetsu (requires TTY)
kugetsu init
# Start the queue daemon (for autonomous operation)
kugetsu queue-daemon start
Normal Workflow
# Enqueue tasks via delegate - agents will process them automatically
kugetsu delegate "work on issue #14"
kugetsu delegate "review PR #92"
# Check queue status
kugetsu queue list # See pending tasks
kugetsu queue stats # See statistics
# Debug queue daemon
kugetsu queue-daemon status # Is daemon running?
kugetsu queue-daemon logs # See daemon logs
# Continue work on existing issue
kugetsu continue github.com/shoko/kugetsu#14 "add tests"
# 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
Queue Daemon Management
# Check if daemon is running
kugetsu queue-daemon status
# View daemon logs for debugging
kugetsu queue-daemon logs
# Restart daemon if needed
kugetsu queue-daemon restart
# Stop daemon
kugetsu queue-daemon stop
Headless Operation
This design solves the headless CLI limitation discovered in Issue #14:
- Problem:
opencode run --session <new>doesn't work headlessly (SSE stream terminates) - 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:
kugetsu listshows tracked sessionskugetsu pruneremoves orphaned files and worktrees- 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:
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
- Add your SSH public key to
~/.ssh/authorized_keyson the container - Configure port forwarding on the host (see docs/kugetsu-setup.md)
- Connect:
ssh -p 2222 <username>@<host-ip>
Remote Usage
Once connected via SSH, kugetsu works the same as local:
kugetsu list
kugetsu start github.com/shoko/kugetsu#14 "fix bug"
kugetsu continue github.com/shoko/kugetsu#14
Documentation
See 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:
chmod +x skills/kugetsu/scripts/tailscale-setup.sh
bash skills/kugetsu/scripts/tailscale-setup.sh <username> <device-name>
The script will:
- Install Tailscale (supports Debian/Ubuntu, Fedora)
- Start the tailscaled daemon
- Prompt for AUTHKEY or browser-based login
- 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 for full Tailscale setup documentation.
Without kugetsu
If kugetsu is not available, use opencode directly:
# 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.