Compare commits

..

7 Commits

Author SHA1 Message Date
5e729227aa Merge pull request 'fix(session): run delegate synchronously when no issue ref is provided' (#261) from fix/issue-254 into main 2026-04-08 13:24:20 +02:00
shokollm
2aa6785f75 fix(session): run delegate synchronously when no issue ref is provided
When kugetsu delegate is called without an issue ref, it now runs
interactively and synchronously instead of fire-and-forget with nohup.

This allows users to see PM response immediately for guidance/creation
requests rather than having the interaction run in background.
2026-04-08 10:36:49 +00:00
d6659ce93a Merge pull request 'feat(session): improve session list and log formatting for human readability' (#260) from fix/issue-252 into main 2026-04-08 08:53:53 +02:00
shokollm
b630d29bdc feat(session): improve session list and log formatting for human readability
- cmd_list: show sessions in table format ordered by last updated time
- cmd_list: display issue_ref, state, last_updated, and session_id columns
- cmd_logs: cleaner timestamp formatting with human-readable dates
- cmd_logs: color-coded log levels (error=red, warn=yellow, info=green, debug=cyan)
- cmd_logs: improved visual separation between log entries

fixes #252
2026-04-08 06:46:11 +00:00
920d714c8f Merge pull request 'fix(session): return proper exit codes for cmd_continue and fork_agent' (#259) from fix/issue-248 into main 2026-04-08 08:42:31 +02:00
c14cb5aec8 Merge pull request 'fix(queue): add --format=json and --limit flags to queue list' (#258) from fix/issue-246 into main 2026-04-08 08:26:49 +02:00
shokollm
b28ad78bd8 fix(queue): add --format=json and --limit flags to queue list 2026-04-08 06:20:52 +00:00
3 changed files with 141 additions and 49 deletions

View File

@@ -56,14 +56,21 @@ Usage:
kugetsu queue [subcommand]
Subcommands:
list Show pending tasks (default)
list [--limit=N] [--format=json] Show pending tasks (default: 10 items, text format)
stats Show queue statistics
clear Clear all queue items
enqueue <issue-ref> <message> Enqueue a task
help Show this help
Options for list:
--limit=N Number of items to return (default: 10)
--format=json Output in JSON format with stats
Examples:
kugetsu queue list
kugetsu queue list --limit=50
kugetsu queue list --format=json
kugetsu queue list --limit=20 --format=json
kugetsu queue stats
kugetsu queue clear
kugetsu queue enqueue github.com/shoko/kugetsu#14 "fix bug"
@@ -355,19 +362,25 @@ PYEOF
get_pending_tasks() {
local limit="${1:-10}"
local format="${2:-text}"
if [ ! -d "$QUEUE_ITEMS_DIR" ]; then
echo "[]"
if [ "$format" = "json" ]; then
echo '{"items": [], "total": 0, "pending": 0}'
else
echo "No pending tasks in queue."
fi
return
fi
python3 -c "
python3 << PYEOF
import json
import os
import sys
queue_dir = os.environ.get('QUEUE_ITEMS_DIR', '')
limit = int(sys.argv[1]) if len(sys.argv) > 1 else 10
format_type = sys.argv[2] if len(sys.argv) > 2 else 'text'
items = []
if os.path.isdir(queue_dir):
@@ -379,13 +392,27 @@ if os.path.isdir(queue_dir):
data = json.load(f)
if data.get('state') == 'pending':
items.append(data)
if len(items) >= limit:
break
except:
pass
print(json.dumps(items))
" "$limit"
items.sort(key=lambda x: x.get('pending_since', ''))
if format_type == 'json':
output = {
"items": items[:limit],
"total": len(items),
"pending": len(items)
}
print(json.dumps(output))
else:
if not items:
print("No pending tasks in queue.")
else:
print("Pending tasks:")
for t in items[:limit]:
msg = t.get('message', '')[:50]
print(f" {t.get('id')}: {t.get('issue_ref')} - {msg}...")
PYEOF
}
get_queue_stats() {
@@ -920,13 +947,24 @@ cmd_queue() {
usage_queue
;;
list)
local pending_tasks=$(get_pending_tasks 10)
if [ "$pending_tasks" = "[]" ]; then
echo "No pending tasks in queue."
else
echo "Pending tasks:"
echo "$pending_tasks" | python3 -c "import sys, json; [print(f\" {t.get('id')}: {t.get('issue_ref')} - {t.get('message', '')[:50]}...\") for t in json.load(sys.stdin)]"
fi
local limit="10"
local format="text"
while [ $# -gt 0 ]; do
case "$1" in
--limit=*)
limit="${1#*=}"
;;
--format=json)
format="json"
;;
*)
;;
esac
shift
done
get_pending_tasks "$limit" "$format"
;;
stats)
local stats=$(get_queue_stats)

View File

@@ -45,22 +45,61 @@ cmd_logs() {
find "$LOGS_DIR" -type f -mtime +7 -delete 2>/dev/null
ls -lt "$LOGS_DIR" | head -$((count + 1)) | tail -$count | while read line; do
echo "$line"
local log_files=()
while IFS= read -r line; do
log_files+=("$line")
done < <(ls -lt "$LOGS_DIR" | head -$((count + 1)) | tail -$count | awk '{print $NF}')
echo "=== Recent Logs ($count files) ==="
echo ""
printf "%-30s %8s %s\n" "FILE" "SIZE" "LAST MODIFIED"
echo "----------------------------------------------------------------"
for log in "${log_files[@]}"; do
if [ -f "$LOGS_DIR/$log" ]; then
local size=$(stat -c %s "$LOGS_DIR/$log" 2>/dev/null || echo "0")
local mtime=$(stat -c %Y "$LOGS_DIR/$log" 2>/dev/null || echo "0")
local date_str=$(date -d "@$mtime" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "unknown")
printf "%-30s %8s %s\n" "$log" "$size" "$date_str"
fi
done
echo ""
echo "Recent log contents:"
echo "===================="
echo "Log Contents:"
echo "================================================================"
for log in $(ls -lt "$LOGS_DIR" | head -$((count + 1)) | tail -$count | awk '{print $NF}'); do
for log in "${log_files[@]}"; do
if [ -f "$LOGS_DIR/$log" ]; then
echo ""
echo "--- $log ---"
echo ">>> $log <<<"
echo "----------------------------------------------------------------"
tail -20 "$LOGS_DIR/$log" | while read line; do
line=$(strip_ansi_codes "$line")
line=$(mask_sensitive_vars "$line")
if [[ "$line" =~ \[([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[^\]]*)\]\ ([a-z]+)\ (.+) ]]; then
local ts="${BASH_REMATCH[1]}"
local level="${BASH_REMATCH[2]}"
local msg="${BASH_REMATCH[3]}"
local color=""
local level_str=""
case "$level" in
error) color="\033[0;31m"; level_str="[ERROR]" ;;
warn) color="\033[0;33m"; level_str="[WARN ]" ;;
info) color="\033[0;32m"; level_str="[INFO ]" ;;
debug) color="\033[0;36m"; level_str="[DEBUG]" ;;
*) color="\033[0;90m"; level_str="[$level]" ;;
esac
if [ -t 1 ]; then
echo -e " \033[0;90m$ts\033[0m ${color}${level_str}\033[0m $msg"
else
echo " $ts ${level_str} $msg"
fi
else
echo " $line"
fi
done
fi
done

View File

@@ -228,8 +228,7 @@ cmd_delegate() {
local msg_file="$LOGS_DIR/msg-$new_session.txt"
printf '%s' "$message" > "$msg_file"
nohup sh -c "GITEA_TOKEN='${GITEA_TOKEN:-}' opencode run '@$msg_file' --session '$new_session'" >> "$log_file" 2>&1 &
echo "Delegated to new session (logged to $(basename "$log_file"))"
sh -c "GITEA_TOKEN='${GITEA_TOKEN:-}' opencode run '@$msg_file' --session '$new_session'" 2>&1 | tee "$log_file"
}
create_session() {
@@ -559,41 +558,49 @@ cmd_list() {
fi
echo ""
echo "Issue sessions:"
local issue_sessions=()
if [ -d "$SESSIONS_DIR" ]; then
for session_file in "$SESSIONS_DIR"/*.json; do
if [ -f "$session_file" ]; then
local filename=$(basename "$session_file")
if [ "$filename" != "base.json" ] && [ "$filename" != "pm-agent.json" ]; then
local mtime=$(stat -c %Y "$session_file" 2>/dev/null || echo "0")
local issue_ref=$(filename_to_issue_ref "$filename")
local opencode_sid=$(python3 -c "import json; print(json.load(open('$session_file')).get('opencode_session_id', 'unknown'))" 2>/dev/null || echo "unknown")
local state=$(python3 -c "import json; print(json.load(open('$session_file')).get('state', 'unknown'))" 2>/dev/null || echo "unknown")
local worktree_path=$(python3 -c "import json; print(json.load(open('$session_file')).get('worktree_path', ''))" 2>/dev/null || echo "")
local worktree_status=""
if [ -n "$worktree_path" ]; then
if [ -d "$worktree_path" ]; then
worktree_status="(worktree exists)"
else
worktree_status="(worktree MISSING)"
fi
fi
echo " $filename"
echo " Issue: $issue_ref"
echo " Session: $opencode_sid"
echo " State: $state"
echo " $worktree_status"
issue_sessions+=("$mtime|$filename|$issue_ref|$opencode_sid|$state|$worktree_path")
fi
fi
done
fi
if [ -d "$WORKTREES_DIR" ]; then
if [ ${#issue_sessions[@]} -gt 0 ]; then
printf "%-45s %-10s %-20s %s\n" "ISSUE_REF" "STATE" "LAST_UPDATED" "SESSION_ID"
echo "-------------------------------------------------------------------------------------------------------------"
while IFS='|' read -r mtime filename issue_ref opencode_sid state worktree_path; do
local last_updated=$(date -d "@$mtime" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "unknown")
local worktree_status=""
if [ -n "$worktree_path" ] && [ ! -d "$worktree_path" ]; then
worktree_status=" [worktree MISSING]"
fi
printf "%-45s %-10s %-20s %s%s\n" "$issue_ref" "$state" "$last_updated" "$opencode_sid" "$worktree_status"
done < <(printf '%s\n' "${issue_sessions[@]}" | sort -t'|' -k1 -rn)
else
echo "No issue sessions found."
fi
echo ""
echo "Worktrees without sessions:"
local orphaned_worktrees=()
if [ -d "$WORKTREES_DIR" ]; then
for worktree in $(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null); do
local worktree_name=$(basename "$worktree")
local has_session=false
if [ -d "$SESSIONS_DIR" ]; then
for session_file in "$SESSIONS_DIR"/*.json; do
if [ -f "$session_file" ]; then
local wt_path=$(python3 -c "import json; print(json.load(open('$session_file')).get('worktree_path', ''))" 2>/dev/null || echo "")
@@ -603,9 +610,17 @@ cmd_list() {
fi
fi
done
if [ "$has_session" = false ]; then
echo " $worktree_name (no session)"
fi
if [ "$has_session" = false ]; then
orphaned_worktrees+=("$worktree_name")
fi
done
fi
if [ ${#orphaned_worktrees[@]} -gt 0 ]; then
echo "Worktrees without sessions:"
for wt in "${orphaned_worktrees[@]}"; do
echo " $wt"
done
fi
}