Compare commits

..

21 Commits

Author SHA1 Message Date
shokollm
dc2254cfba fix(kugetsu): add post-comment helper for PM agent
Add 'kugetsu post-comment' command to post issue comments without
writing to /tmp. This solves #45 where PM agent cannot write to /tmp
for comment posting.

Usage:
  kugetsu post-comment <repo-url> <issue-number> <body>

The command reads GITEA_TOKEN from:
1. GITEA_TOKEN environment variable
2. ~/.kugetsu/env/pm-agent.env

Example:
  kugetsu post-comment https://git.fbrns.co/shoko/kugetsu 45 "Comment text"

Fixes #45
2026-04-03 14:26:32 +00: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
2 changed files with 319 additions and 40 deletions

Submodule .kugetsu-worktrees/git.fbrns.co-shoko-jigaido-2 added at 332d7fc60a

View File

@@ -394,6 +394,41 @@ kugetsu_get_pm_context() {
fi fi
} }
kugetsu_get_fork_context() {
local issue_ref="$1"
local context=""
context="## IMPORTANT WORKING RULES
1. You are working on issue: $issue_ref
2. If you encounter ANY error, blocker, or cannot complete the task:
- STOP immediately
- Log what happened and why you cannot proceed
- Do NOT switch to other work or try alternative approaches
3. Do NOT work on other issues or PRs unless explicitly asked
4. Environment variables are available in ~/.kugetsu/env/
"
if [ -f "$REPOS_CONFIG" ]; then
context="${context}
## REPOSITORIES CONFIG
$(cat "$REPOS_CONFIG")
"
fi
if [ -f "$ENV_DIR/default.env" ]; then
context="${context}
## ENVIRONMENT (available at ~/.kugetsu/env/)
Environment file exists at: $ENV_DIR/default.env
Source it with: source ~/.kugetsu/env/default.env
"
fi
echo "$context"
}
kugetsu_add_notification() { kugetsu_add_notification() {
local type="$1" local type="$1"
local message="$2" local message="$2"
@@ -606,6 +641,99 @@ EOF
fi fi
} }
parse_issue_ref_from_message() {
local message="$1"
local gitserver=""
local owner=""
local repo=""
local issue_number=""
if echo "$message" | grep -qE '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(issues|pull)/[0-9]+'; then
gitserver=$(echo "$message" | grep -oE '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+' | head -1 | sed 's/\/[^/]*\/[^/]*$//')
local full_path=$(echo "$message" | grep -oE '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(issues|pull)/[0-9]+' | head -1)
owner=$(echo "$full_path" | cut -d'/' -f2)
repo=$(echo "$full_path" | cut -d'/' -f3)
issue_number=$(echo "$full_path" | grep -oE '[0-9]+$' | head -1)
elif echo "$message" | grep -qE '[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#([0-9]+)'; then
owner=$(echo "$message" | grep -oE '[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#' | sed 's/#$//' | cut -d'/' -f1)
repo=$(echo "$message" | grep -oE '[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#' | sed 's/#$//' | cut -d'/' -f2)
issue_number=$(echo "$message" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -1)
fi
echo "${gitserver}|${owner}|${repo}|${issue_number}"
}
get_missing_info() {
local parsed="$1"
local gitserver=$(echo "$parsed" | cut -d'|' -f1)
local owner=$(echo "$parsed" | cut -d'|' -f2)
local repo=$(echo "$parsed" | cut -d'|' -f3)
local issue_number=$(echo "$parsed" | cut -d'|' -f4)
local missing=""
[ -z "$gitserver" ] && missing="${missing}git server, "
[ -z "$owner" ] && missing="${missing}owner, "
[ -z "$repo" ] && missing="${missing}repository, "
[ -z "$issue_number" ] && missing="${missing}issue number, "
echo "$missing" | sed 's/, $//'
}
build_missing_info_context() {
local missing="$1"
if [ -n "$missing" ]; then
echo ""
echo "NOTE: This task delegation has no information about: ${missing}."
echo "We need them if user wants to work on a specific issue. Otherwise we don't need it."
fi
}
find_worktrees_by_issue_number() {
local issue_number="$1"
local results=""
if [ ! -d "$WORKTREES_DIR/.kugetsu-worktrees" ]; then
echo ""
return
fi
for wt in "$WORKTREES_DIR/.kugetsu-worktrees"/*; do
if [ -d "$wt" ]; then
local wt_issue_number=$(echo "$wt" | grep -oE '#?[0-9]+$' | grep -oE '[0-9]+' | head -1)
if [ "$wt_issue_number" = "$issue_number" ]; then
results="${results}${wt}:worktree
"
fi
fi
done
echo "$results"
}
find_sessions_by_issue_number() {
local issue_number="$1"
local results=""
if [ ! -d "$SESSIONS_DIR" ]; then
echo ""
return
fi
for session_file in "$SESSIONS_DIR"/*.json; do
if [ -f "$session_file" ]; then
local session_issue_ref=$(basename "$session_file" .json | sed 's/_/\//g')
local session_issue_number=$(echo "$session_issue_ref" | grep -oE '#?[0-9]+$' | grep -oE '[0-9]+' | head -1)
if [ "$session_issue_number" = "$issue_number" ]; then
results="${results}${session_file}:session
"
fi
fi
done
echo "$results"
}
cmd_delegate() { cmd_delegate() {
local message="${1:-}" local message="${1:-}"
local verbosity="${KUGETSU_VERBOSITY:-default}" local verbosity="${KUGETSU_VERBOSITY:-default}"
@@ -625,6 +753,70 @@ cmd_delegate() {
mkdir -p "$LOGS_DIR" mkdir -p "$LOGS_DIR"
local log_file="$LOGS_DIR/delegate-$(date +%s).log" local log_file="$LOGS_DIR/delegate-$(date +%s).log"
local parsed=$(parse_issue_ref_from_message "$message")
local gitserver=$(echo "$parsed" | cut -d'|' -f1)
local owner=$(echo "$parsed" | cut -d'|' -f2)
local repo=$(echo "$parsed" | cut -d'|' -f3)
local issue_number=$(echo "$parsed" | cut -d'|' -f4)
local missing_info=$(get_missing_info "$parsed")
local context_injection=""
if [ -n "$missing_info" ]; then
context_injection=$(build_missing_info_context "$missing_info")
echo "NOTE: Delegation missing information: ${missing_info}"
fi
local candidates=""
local candidate_count=0
if [ -n "$issue_number" ]; then
local worktrees=$(find_worktrees_by_issue_number "$issue_number")
local sessions=$(find_sessions_by_issue_number "$issue_number")
while IFS=: read -r path type; do
if [ -n "$path" ]; then
candidate_count=$((candidate_count + 1))
candidates="${candidates}${candidate_count}) ${path} (${type})
"
fi
done <<< "$worktrees"
while IFS=: read -r path type; do
if [ -n "$path" ]; then
candidate_count=$((candidate_count + 1))
candidates="${candidates}${candidate_count}) ${path} (${type})
"
fi
done <<< "$sessions"
fi
local use_worktree=""
if [ $candidate_count -gt 0 ]; then
echo "Found $candidate_count existing worktree(s)/session(s) for issue #${issue_number}:"
echo "$candidates"
echo "r) Delegate anyway (without routing)"
echo "Which one to use? [1-${candidate_count}/r]: "
read -r choice
if [ "$choice" = "r" ] || [ -z "$choice" ]; then
use_worktree=""
elif [ "$choice" -ge 1 ] && [ "$choice" -le "$candidate_count" ]; then
local selected=$(echo "$candidates" | sed -n "${choice}p")
use_worktree=$(echo "$selected" | sed 's/) .*//')
fi
fi
local final_message="${message}${context_injection}"
if [ -n "$use_worktree" ]; then
if [ -d "$use_worktree" ]; then
echo "Using worktree: $use_worktree"
final_message="${final_message}
NOTE: Worktree selected: ${use_worktree}"
fi
fi
local temp_dir="${KUGETSU_TEMP_DIR:-$HOME/.local/share/opencode/tool-output}" local temp_dir="${KUGETSU_TEMP_DIR:-$HOME/.local/share/opencode/tool-output}"
mkdir -p "$ENV_DIR" mkdir -p "$ENV_DIR"
@@ -636,7 +828,7 @@ cmd_delegate() {
fi fi
env_sh="${env_sh}set +a; " env_sh="${env_sh}set +a; "
nohup sh -c "${env_sh}opencode run '$message' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 & nohup sh -c "${env_sh}opencode run '${final_message}' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 &
disown disown
echo "Delegated to PM agent (logged to $(basename "$log_file"))" echo "Delegated to PM agent (logged to $(basename "$log_file"))"
echo "Verbosity: $verbosity" echo "Verbosity: $verbosity"
@@ -876,7 +1068,7 @@ cmd_doctor() {
} }
fix_session_permissions() { fix_session_permissions() {
local opencode_db="${OPENCODE_DB:-$HOME/.opencode/opencode.db}" local opencode_db="${OPENCODE_DB:-$HOME/.local/share/opencode/opencode.db}"
if [ ! -f "$opencode_db" ]; then if [ ! -f "$opencode_db" ]; then
echo "[ERROR] opencode database not found: $opencode_db" echo "[ERROR] opencode database not found: $opencode_db"
@@ -1088,6 +1280,11 @@ EOF
echo "Created pm-agent env file: $ENV_DIR/pm-agent.env" echo "Created pm-agent env file: $ENV_DIR/pm-agent.env"
fi fi
if [ -d "$LOGS_DIR" ]; then
echo "Cleaning up old logs..."
rm -rf "$LOGS_DIR"/*.log 2>/dev/null || true
fi
local existing_base=$(get_base_session_id) local existing_base=$(get_base_session_id)
local existing_pm=$(get_pm_agent_session_id) local existing_pm=$(get_pm_agent_session_id)
@@ -1107,6 +1304,29 @@ EOF
exit 1 exit 1
fi fi
local init_worktree_dir="$HOME/.kugetsu-worktrees"
mkdir -p "$init_worktree_dir"
cd "$init_worktree_dir"
echo "Initialized kugetsu worktrees directory: $init_worktree_dir"
echo "Base session will be created in this directory."
echo ""
local cwd_files=$(ls -A "$PWD" 2>/dev/null | wc -l)
local cwd_git=$(git rev-parse --is-inside-work-tree 2>/dev/null || echo "false")
if [ "$cwd_files" -gt 0 ] || [ "$cwd_git" = "true" ]; then
echo "Warning: Worktrees directory is not empty: $PWD" >&2
echo "This may cause project context to contaminate the base session." >&2
echo "Consider running kugetsu destroy --base -y and reinitializing." >&2
echo "" >&2
echo "Files in current directory: $cwd_files" >&2
if [ "$cwd_git" = "true" ]; then
echo "Git repository detected: $(git rev-parse --show-toplevel 2>/dev/null || echo 'unknown')" >&2
fi
echo "" >&2
echo "Press Ctrl+C to cancel or wait 5 seconds to continue anyway..." >&2
sleep 5
fi
echo "Starting TUI to create base session..." echo "Starting TUI to create base session..."
echo "Press Ctrl+C to cancel or wait for session to be created" echo "Press Ctrl+C to cancel or wait for session to be created"
sleep 2 sleep 2
@@ -1226,12 +1446,6 @@ cmd_start() {
local session_file="$(issue_ref_to_filename "$issue_ref").json" local session_file="$(issue_ref_to_filename "$issue_ref").json"
# Get list of sessions before fork to compare against after
declare -a before_sessions=()
while IFS= read -r sess; do
before_sessions+=("$sess")
done < <(opencode session list 2>/dev/null | grep -oP '^ses_\w+')
echo "Forking session for '$issue_ref'..." echo "Forking session for '$issue_ref'..."
# Session-counting: count actual dev sessions, reject if at limit # Session-counting: count actual dev sessions, reject if at limit
@@ -1243,44 +1457,92 @@ cmd_start() {
exit 1 exit 1
fi fi
local fork_log="$SESSIONS_DIR/$session_file.fork.log"
local opencode_db="${OPENCODE_DB:-$HOME/.local/share/opencode/opencode.db}"
> "$fork_log"
local fork_context=$(kugetsu_get_fork_context "$issue_ref")
local full_message="${fork_context}
## YOUR TASK
$message"
fix_session_permissions
if [ "$DEBUG_MODE" = true ]; then if [ "$DEBUG_MODE" = true ]; then
(cd "$worktree_path" && opencode run "$message" --fork --session "$base_session_id" 2>&1) | tee "$SESSIONS_DIR/$session_file.debug.log" & (cd "$worktree_path" && opencode run "$full_message" --fork --session "$base_session_id" --dir "$worktree_path" 2>&1) | tee "$fork_log" &
else else
(cd "$worktree_path" && opencode run "$message" --fork --session "$base_session_id" 2>&1) & (cd "$worktree_path" && opencode run "$full_message" --fork --session "$base_session_id" --dir "$worktree_path" 2>&1) >> "$fork_log" &
fi fi
# Wait briefly for session to be created local fork_pid=$!
local max_attempts=10
local attempt=1
local new_session_id=""
local fork_log_output=""
while [ $attempt -le $max_attempts ]; do
sleep 1 sleep 1
# Find the new session by comparing before/after lists new_session_id=$(python3 -c "
# Skip any session that existed before the fork and skip base/pm-agent import sqlite3
local new_session_id="" conn = sqlite3.connect('$opencode_db')
while IFS= read -r sess; do cursor = conn.cursor()
# Skip base and pm-agent cursor.execute(\"SELECT id FROM session WHERE directory = '$worktree_path' ORDER BY time_created DESC LIMIT 1\")
[ "$sess" = "$base_session_id" ] && continue result = cursor.fetchone()
[ "$sess" = "$pm_agent_session_id" ] && continue if result:
print(result[0])
" 2>/dev/null || echo "")
# Check if this session existed before if [ -n "$new_session_id" ] && [ "$new_session_id" != "$base_session_id" ] && [ "$new_session_id" != "$pm_agent_session_id" ]; then
local existed_before=false
for before_sess in "${before_sessions[@]}"; do
if [ "$sess" = "$before_sess" ]; then
existed_before=true
break break
fi fi
if ! kill -0 $fork_pid 2>/dev/null; then
fork_log_output=$(tail -20 "$fork_log" 2>/dev/null || echo "(log empty or unavailable)")
break
fi
attempt=$((attempt + 1))
done done
if [ "$existed_before" = false ]; then
new_session_id="$sess"
break
fi
done < <(opencode session list 2>/dev/null | grep -oP '^ses_\w+')
if [ -z "$new_session_id" ]; then if [ -z "$new_session_id" ]; then
echo "Error: Could not find newly created session" >&2 echo "Error: Could not find newly created session after ${max_attempts}s" >&2
if [ -n "$fork_log_output" ]; then
echo "Fork log output:" >&2
echo "$fork_log_output" >&2
fi
remove_worktree_for_issue "$issue_ref" remove_worktree_for_issue "$issue_ref"
exit 1 exit 1
fi fi
echo "Updating permissions for new session: $new_session_id"
python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
PERMISSION_JSON = '[{\"permission\":\"question\",\"pattern\":\"*\",\"action\":\"deny\"},{\"permission\":\"plan_enter\",\"pattern\":\"*\",\"action\":\"deny\"},{\"permission\":\"plan_exit\",\"pattern\":\"*\",\"action\":\"deny\"},{\"permission\":\"external_directory\",\"pattern\":\"*\",\"action\":\"allow\"}]'
cursor.execute('UPDATE session SET permission = ? WHERE id = ?', (PERMISSION_JSON, '$new_session_id'))
conn.commit()
print('[OK] Session permissions updated')
"
if [ "$DEBUG_MODE" = true ]; then
echo "[DEBUG] Forked session permissions check:"
python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
cursor.execute(\"SELECT id, directory, permission FROM session WHERE id = '$new_session_id'\")
for row in cursor.fetchall():
print(' ID:', row[0])
print(' Directory:', row[1])
print(' Permission:', row[2])
" 2>/dev/null || echo " (failed to query DB)"
fi
printf '{"type": "forked", "issue_ref": "%s", "opencode_session_id": "%s", "worktree_path": "%s", "created_at": "%s", "state": "idle"}\n' \ printf '{"type": "forked", "issue_ref": "%s", "opencode_session_id": "%s", "worktree_path": "%s", "created_at": "%s", "state": "idle"}\n' \
"$issue_ref" "$new_session_id" "$worktree_path" "$(date -Iseconds)" > "$SESSIONS_DIR/$session_file" "$issue_ref" "$new_session_id" "$worktree_path" "$(date -Iseconds)" > "$SESSIONS_DIR/$session_file"
@@ -1334,12 +1596,13 @@ cmd_continue() {
echo "Continuing session for '$session_name'..." echo "Continuing session for '$session_name'..."
# Note: --continue always allowed (existing sessions don't count toward limit) # Note: --continue always allowed (existing sessions don't count toward limit)
# Wrap in subshell with cd to ensure worktree directory is set correctly in session DB
if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then
echo "Using worktree: $worktree_path" echo "Using worktree: $worktree_path"
if [ "$DEBUG_MODE" = true ]; then if [ "$DEBUG_MODE" = true ]; then
(cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" 2>&1) | tee "$session_path.debug.log" & (cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" --dir "$worktree_path" 2>&1) | tee "$session_path.debug.log" &
else else
(cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" 2>&1) & (cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" --dir "$worktree_path" 2>&1) &
fi fi
else else
if [ "$DEBUG_MODE" = true ]; then if [ "$DEBUG_MODE" = true ]; then
@@ -1496,15 +1759,22 @@ cmd_destroy() {
if [ "$target" = "base" ]; then if [ "$target" = "base" ]; then
if [ "$force" = true ]; then if [ "$force" = true ]; then
local base_session_id=$(get_base_session_id)
local pm_agent_session_id=$(get_pm_agent_session_id)
rm -f "$SESSIONS_DIR/base.json" rm -f "$SESSIONS_DIR/base.json"
local pm_agent=$(get_pm_agent_session_id)
if [ -n "$pm_agent" ] && [ "$pm_agent" != "null" ]; then
rm -f "$SESSIONS_DIR/pm-agent.json" rm -f "$SESSIONS_DIR/pm-agent.json"
rm -f "$SESSIONS_DIR/issue-"*.json 2>/dev/null || true
echo '{"base": null, "pm_agent": null, "issues": {}}' > "$INDEX_FILE" echo '{"base": null, "pm_agent": null, "issues": {}}' > "$INDEX_FILE"
else
echo '{"base": null, "pm_agent": null, "issues": {}}' > "$INDEX_FILE" if [ -n "$base_session_id" ] && [ "$base_session_id" != "null" ]; then
echo "Deleting base session: $base_session_id"
opencode session delete "$base_session_id" 2>/dev/null || echo "Warning: Could not delete base session"
fi fi
echo "Base session destroyed" if [ -n "$pm_agent_session_id" ] && [ "$pm_agent_session_id" != "null" ]; then
echo "Deleting PM agent session: $pm_agent_session_id"
opencode session delete "$pm_agent_session_id" 2>/dev/null || echo "Warning: Could not delete PM agent session"
fi
echo "Base and PM agent sessions destroyed"
else else
echo "Error: destroying base session requires --base -y" >&2 echo "Error: destroying base session requires --base -y" >&2
exit 1 exit 1
@@ -1514,6 +1784,7 @@ cmd_destroy() {
if [ "$target" = "pm-agent" ]; then if [ "$target" = "pm-agent" ]; then
if [ "$force" = true ]; then if [ "$force" = true ]; then
local pm_session_id=$(get_pm_agent_session_id)
rm -f "$SESSIONS_DIR/pm-agent.json" rm -f "$SESSIONS_DIR/pm-agent.json"
local base=$(get_base_session_id) local base=$(get_base_session_id)
if [ -n "$base" ] && [ "$base" != "null" ]; then if [ -n "$base" ] && [ "$base" != "null" ]; then
@@ -1521,6 +1792,10 @@ cmd_destroy() {
else else
write_index "null" "null" "{}" write_index "null" "null" "{}"
fi fi
if [ -n "$pm_session_id" ] && [ "$pm_session_id" != "null" ]; then
echo "Deleting opencode session: $pm_session_id"
opencode session delete "$pm_session_id" 2>/dev/null || echo "Warning: Could not delete session from opencode (may already be deleted)"
fi
echo "PM agent session destroyed" echo "PM agent session destroyed"
else else
echo "Error: destroying pm-agent session requires --pm-agent -y" >&2 echo "Error: destroying pm-agent session requires --pm-agent -y" >&2
@@ -1597,6 +1872,9 @@ main() {
env) env)
cmd_env "$@" cmd_env "$@"
;; ;;
post-comment)
cmd_post_comment "$@"
;;
doctor) doctor)
cmd_doctor "$@" cmd_doctor "$@"
;; ;;