Compare commits

..

36 Commits

Author SHA1 Message Date
shokollm
54aa6419eb fix(kugetsu): prevent excess agent spawning with flock + sequential processing
- 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
2026-04-05 08:44:45 +00:00
98a31070a7 Merge pull request '[FIX] process_queue: add missing closing parentheses' (#143) from fix/issue-142-process-queue-missing-parens into main 2026-04-05 08:56:59 +02:00
26346235c9 fix: add missing closing parenthesis in process_queue Python extraction
Fixes #142 - process_queue silently skips all queue items because
issue_ref and message Python extraction commands were missing a closing
parenthesis. The error was silently swallowed by 2>/dev/null causing
both variables to be empty, so every queue item was skipped.
2026-04-05 06:51:57 +00:00
2212fabf22 Merge pull request 'feat(timeout): add agent timeout handling' (#141) from feat/agent-timeout into main 2026-04-05 06:59:05 +02:00
shokollm
0fa778353b feat(timeout): add agent timeout handling
Implements #137 - Agent timeout handling.

Changes:
- Add TASK_TIMEOUT_HOURS config (default: 1 hour)
- Update queue item to track opencode_session_id and pid
- Add check_task_timeouts() function that:
  - Checks notified tasks against timeout threshold
  - Kills process if exceeded
  - Marks session as 'timeout' state
- Integrate timeout check into queue daemon loop

Timeout behavior:
- Task is marked 'notified' when PM receives it
- If not completed within TASK_TIMEOUT_HOURS, task is killed
- Queue item marked 'error', session marked 'timeout'
2026-04-05 04:53:27 +00:00
151efadca3 Merge pull request 'feat(queue): add queue system with background daemon' (#140) from feat/queue-daemon into main 2026-04-05 06:49:13 +02:00
shokollm
379d53cedc docs: update SKILL.md with queue system documentation
- Update kugetsu delegate section to explain queue-based processing
- Add queue-daemon command documentation
- Update queue command with proper list/stats/clear/enqueue
- Add queue-related config options
- Update directory structure to include queue/
- Update workflow example with queue daemon setup
2026-04-05 04:45:56 +00:00
shokollm
043542344a feat(queue): add queue system with background daemon
Implements #134 - Queue system with background daemon.

## Changes

### Configuration
- QUEUE_DIR, QUEUE_ITEMS_DIR for queue storage
- QUEUE_DAEMON_PID_FILE, LOCK_FILE, LOG_FILE for daemon management
- QUEUE_DAEMON_INTERVAL_MINUTES (default: 5)
- QUEUE_DAEMON_BATCH_SIZE (default: 2)
- QUEUE_CLEANUP_AGE_DAYS (default: 7)

### Queue System
- File-based queue at ~/.kugetsu/queue/items/
- One JSON file per queue item
- States: pending, notified, completed, error

### New Commands
- kugetsu queue [list|stats|clear] - View queue status
- kugetsu queue enqueue <issue-ref> <message> - Manually enqueue
- kugetsu queue-daemon [start|stop|restart|status|logs] - Daemon management

### Behavior Change
- kugetsu delegate now always enqueues (fire-and-forget)
- Queue daemon polls queue and invokes PM when slots available

### Queue Item Format
```json
{
  "id": "q_xxx",
  "issue_ref": "github.com/user/repo#123",
  "message": "task description",
  "state": "pending",
  "pending_since": "...",
  "notified_at": null,
  "completed_at": null,
  "error": null
}
```

Closes #134
2026-04-05 04:28:41 +00:00
e763ceb0ad Merge pull request 'feat(context): add context dump/load for session isolation' (#139) from feat/context-dump-load into main 2026-04-05 06:23:40 +02:00
shokollm
61f06f825f Add context dump/load feature
Adds session context management to prevent session poisoning:
- CONTEXT_DIR and ENABLE_CONTEXT_DUMP config options
- issue_ref_to_context_file() - derive context file path
- kugetsu_context_load() - load previous context
- kugetsu_context_dump() - save context on session start
- kugetsu_context_update_message() - append to conversation history
- Integration in cmd_start and cmd_continue
- New 'kugetsu context' command
2026-04-05 04:23:26 +00:00
b76a9b883a Merge pull request 'feat(worktree-lifecycle): add PR tracking and safe destroy' (#138) from feat/worktree-lifecycle into main 2026-04-05 06:00:15 +02:00
shokollm
ac850869fd fix(worktree-lifecycle): use github.com as example in set-pr help
- Remove accidentally committed worktree directory
2026-04-05 03:56:48 +00:00
shokollm
3107dbf1e5 fix(worktree-lifecycle): use GIT_SERVERS config for check_pr_status
- Extract hostname from pr_url instead of hardcoding domains
- Look up server base URL from GIT_SERVERS config
- Append /api/v1 to derive API URL (configurable per server)
- Works with any server configured in GIT_SERVERS
2026-04-05 03:41:41 +00:00
shokollm
b8b97e3c09 fix(worktree-lifecycle): address PR review feedback
- Rename update-pr to set-pr for clarity (it's setting the PR URL, not updating PR)
- Add optional pr-url argument to kugetsu start command
  Usage: kugetsu start <issue-ref> <message> [pr-url]
- If pr-url is provided at start, it's stored directly in session file
2026-04-05 03:16:05 +00:00
shokollm
d8af560e6d feat(worktree-lifecycle): add PR tracking and safe destroy
- Add WORKTREE_CHECK_PR_STATUS config (default: true)
- Add pr_url and branch_name fields to session files
- Add check_pr_status() to query PR status via API (Gitea/GitHub)
- Add update_session_pr_url() to update PR URL in session
- Add kugetsu update-pr command to set PR URL
- Modify cmd_destroy to check PR status before destroying worktree

Closes #135
2026-04-05 02:50:09 +00:00
5d12f6ca42 Merge pull request 'feat(kugetsu): smart delegate with worktree awareness' (#130) from feature/smart-delegate-worktree-awareness into main 2026-04-03 16:31:30 +02:00
shokollm
91505345a2 feat(kugetsu): smart delegate with worktree awareness
- Parse issue refs from message (gitserver.com/owner/repo/issues/123 or owner/repo#123)
- Find existing worktrees/sessions by issue number
- Ask user to confirm which worktree to use, or delegate anyway
- Inject missing info context to PM agent
- Inject selected worktree context to PM agent

Fixes #128
2026-04-03 14:20:48 +00:00
f7fe22de25 Merge pull request 'fix(kugetsu): wrap cmd_continue in subshell with cd for correct worktree dir' (#129) from fix/cmd-continue-worktree-dir into main 2026-04-03 15:58:19 +02:00
shokollm
3ce43ffa65 fix(kugetsu): wrap cmd_continue in subshell with cd for correct worktree dir
The --dir flag only sets directory for the subprocess, not the session's
stored directory in opencode's SQLite DB. This was already fixed for
cmd_start in v0.1.10, but cmd_continue still had the bug.

Fixes #127
2026-04-03 13:06:02 +00:00
shokollm
416e8e5757 fix(kugetsu): destroy --base now also deletes PM agent session
When destroying base session, we now also delete the PM agent session
and all issue session files. This ensures clean slate on re-init.
2026-04-02 14:47:40 +00:00
1c1d18b9ae Merge pull request 'fix(kugetsu): init creates base session in ~/.kugetsu-worktrees and adds context to forked sessions' (#114) from fix/session-context-and-init-worktree into main 2026-04-02 16:35:12 +02:00
shokollm
8c639e2928 fix(kugetsu): init creates base session in ~/.kugetsu-worktrees, adds context to forked sessions, and clears logs
1. Init: cd to ~/.kugetsu-worktrees before creating base session
   This keeps all worktrees inside a predictable directory structure
   and avoids external_directory permission issues

2. Init: Clear old logs but keep repos.json, config, and env files

3. Fork context: Add kugetsu_get_fork_context() that provides:
   - Important working rules (stop on error, don't pivot)
   - Repository configuration from repos.json
   - Environment file location info

4. Fork message: Prepend context to user message when forking session
2026-04-02 14:32:07 +00:00
c4c3556247 Merge pull request 'fix(kugetsu): destroy --base and --pm-agent actually delete opencode sessions' (#113) from fix/destroy-removes-opencode-session into main 2026-04-02 15:30:48 +02:00
shokollm
4342347ac6 fix(kugetsu): destroy --base and --pm-agent actually delete opencode sessions
Previously destroy only removed local session files but didn't delete
the sessions from opencode's database. This caused init to reuse the
same session with old context.

Now destroy calls 'opencode session delete <id>' to properly remove
the session from opencode.
2026-04-02 13:28:47 +00:00
7888a34bd9 Merge pull request 'fix(kugetsu): warn if init run from non-empty directory' (#112) from fix/init-directory-warning into main 2026-04-02 15:21:30 +02:00
shokollm
e2a37cdbb9 fix(kugetsu): warn if init run from non-empty directory
Warn users if running kugetsu init from a directory with files or
git repository. This prevents project context from contaminating the
base session, which causes forked sessions to have unwanted context.
2026-04-02 13:19:49 +00:00
shokollm
6e9472b5e2 fix(kugetsu): detect session via DB query instead of opencode session list
opencode session list doesn't show sessions in ~/.kugetsu-worktrees/ directories.
This caused detection to fail even though sessions were being created.

Now we query the database directly for sessions matching the worktree path.
Also fixed database path in fix_session_permissions (was ~/.opencode/, should be ~/.local/share/opencode/).
2026-04-02 11:45:35 +00:00
shokollm
775f73348a fix(kugetsu): update forked session permissions after detection
Previously we only fixed base session permissions before forking.
But permissions are NOT inherited from parent to child.

Now we update the newly created session's permissions immediately
after detection, ensuring the forked session can access external
directories like ~/.kugetsu/worktrees/.
2026-04-02 11:15:27 +00:00
2e9081f4f5 Merge pull request 'fix(kugetsu): call fix_session_permissions before forking' (#109) from fix/prefork-permissions into main 2026-04-02 13:10:54 +02:00
shokollm
f7ac2f35fe fix(kugetsu): call fix_session_permissions before forking
- Call fix_session_permissions in cmd_start before forking to ensure
  base session has correct permissions for external_directory access
- Add debug logging to show forked session's directory and permissions
  after creation to help diagnose permission inheritance issues
2026-04-02 11:08:30 +00:00
97d7511e56 Merge pull request 'fix(kugetsu): session detection ordering bug and debugging' (#108) from fix/session-detection-v2 into main 2026-04-02 12:26:57 +02:00
shokollm
cd12a0cda8 fix(kugetsu): fix session detection ordering and add DB debugging
1. Move session detection BEFORE checking if fork process is still running.
   Previous code broke out of loop if forked process exited, skipping detection.

2. Add database query debugging when detection fails to help diagnose
   why opencode session list might miss newly created sessions.
2026-04-02 09:57:27 +00:00
ffdf5e34c8 Merge pull request 'fix(kugetsu): improve session detection in cmd_start with retry logic and logging' (#107) from fix/start-session-detection into main 2026-04-02 11:41:52 +02:00
shokollm
b3ac73a283 Merge origin/main into fix/start-session-detection
Resolve conflict: use cd approach for worktree, keep retry logic
2026-04-02 09:39:45 +00:00
shokollm
1128b3dfa8 fix(kugetsu): improve session detection in cmd_start with retry logic and logging
- Capture fork output to log file for debugging
- Track fork PID to detect if process exits early
- Retry session detection up to 10 seconds instead of 1 second
- Show fork log output when session creation fails
- Improve error message to indicate timeout
2026-04-02 09:29:30 +00:00
90f46a778a Merge pull request 'fix: use cd + worktree inside parent dir instead of --dir flag (fixes #105)' (#106) from fix/worktree-isolation-via-cd into main 2026-04-02 10:29:32 +02:00
3 changed files with 1238 additions and 106 deletions

View File

@@ -0,0 +1,67 @@
# Fix: Queue daemon spawning excess agents due to race condition
## Problem
When enqueueing multiple tasks (e.g., 6 tasks), the queue daemon was spawning many more subagents than expected, eventually exhausting container memory.
**Root Cause:** The combination of:
1. `process_queue()` calling `opencode run` directly instead of `kugetsu start`, bypassing all concurrency logic
2. `count_active_dev_sessions()` counting `pm-agent.json` toward `MAX_CONCURRENT_AGENTS`, reducing effective dev agent slots
3. No atomic locking around session count check + session file creation (TOCTOU race condition)
4. Background spawning of multiple concurrent processes in `process_queue()`
**Expected behavior:** With `MAX_CONCURRENT_AGENTS=3` and 6 tasks:
- Tasks should be processed sequentially via `kugetsu start`
- Only 3 dev agents should run at a time
- Tasks should queue and wait for slots to free up
## Solution
### 1. `count_active_dev_sessions()` - Exclude pm-agent
Only count actual dev agent session files (exclude `pm-agent.json`).
### 2. `process_queue()` - Call `kugetsu start` directly + retry logic
- Call `kugetsu start` directly (foreground, sequential) instead of spawning `opencode run` background process
- Dynamic batch size = available slots (removes need for `QUEUE_DAEMON_BATCH_SIZE`)
- Retry logic (max 3 attempts) on failure
- On failure: cleanup worktree/session and revert to `pending` state
- Save `fork_pid` to queue item for timeout handling
### 3. `cmd_start()` - Add flock
- Add flock around critical section (count check + fork)
- Track `fork_pid` for queue item timeout handling
### 4. Notification System
New notification types:
| Event | Type |
|-------|------|
| Task enqueued | `task_queued` |
| Task dequeued | `task_dequeued` |
| Task started | `task_started` |
| Task completed | `task_completed` |
| Task error | `task_error` |
### 5. Config
- Remove `QUEUE_DAEMON_BATCH_SIZE` (no longer needed - batch size is now dynamic)
## Notification Flow
| Event | Location | Type |
|-------|----------|------|
| Task enqueued | `enqueue_task()` | `task_queued` |
| Task dequeued | `process_queue()` after state change to `notified` | `task_dequeued` |
| Task started | `cmd_start()` after session file created | `task_started` |
| Task completed | `update_queue_item_state()` | `task_completed` |
| Task error | `update_queue_item_state()` | `task_error` |
## Out of Scope
- Re-check loop in cmd_start (checking if session DB is reliable) - deferred to separate research issue
- Buffer mechanism for excess forking (safety failsafe only)
## Status
- [x] Issue created
- [x] Implementation
- [x] PR created (#147)
- [ ] Merged

View File

@@ -49,6 +49,8 @@ A default config file is created during `kugetsu init` with commented examples:
| `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
@@ -111,6 +113,10 @@ Each issue session gets its own git worktree to prevent conflicts:
├── 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
```
@@ -258,16 +264,17 @@ kugetsu destroy --base -y
### kugetsu delegate `<message>`
Send a message to the PM agent for task coordination (fire-and-forget):
Send a message to the PM agent for task coordination via queue:
```bash
kugetsu delegate "work on issue #14"
kugetsu delegate "review PR #92"
```
- Non-blocking: returns immediately, runs in background
- PM agent processes the message asynchronously
- Uses `KUGETSU_VERBOSITY` env var to control PM agent output verbosity
- Log output stored in `~/.kugetsu/logs/delegate-<timestamp>.log`
- **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 list` to see pending tasks
- Use `kugetsu queue-daemon logs` to debug queue processing
### kugetsu logs [n]
@@ -328,35 +335,79 @@ kugetsu server default github # Set default server
kugetsu server get github # Get server URL
```
### kugetsu queue <list|enqueue|dequeue|clear>
### kugetsu queue <list|stats|clear>
Manage task queue for autonomous PM operation:
```bash
kugetsu queue list # Show queued tasks
kugetsu queue enqueue "task" # Add task to queue
kugetsu queue dequeue # Remove next task from queue
kugetsu queue clear # Clear all queued tasks
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 stored in `~/.kugetsu/queue.json`
**Queue Item States:**
- `pending` - Waiting in queue, daemon can pick up
- `notified` - PM agent has picked up the task
- `completed` - Dev agent finished, PR created
- `error` - Timeout or failure
### kugetsu queue-daemon <start|stop|restart|status|logs>
Manage the queue daemon background process:
```bash
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:**
1. Runs at configurable interval (default: 5 minutes)
2. Checks if active agents < MAX_CONCURRENT_AGENTS
3. Picks 1-N pending items (configurable batch size)
4. Forks PM session for each picked item
5. PM decides whether to use `start` or `continue`
**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
```bash
# First-time setup (requires TTY)
# Initialize kugetsu (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/
# Start the queue daemon (for autonomous operation)
kugetsu queue-daemon start
```
# Continue later
### Normal Workflow
```bash
# 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"
# Continue again
kugetsu continue github.com/shoko/kugetsu#14 "fix failing test"
# List all sessions
kugetsu list
@@ -367,6 +418,21 @@ kugetsu prune --force
kugetsu destroy github.com/shoko/kugetsu#14
```
### Queue Daemon Management
```bash
# 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:

File diff suppressed because it is too large Load Diff