- README.md nou cu documentație completă: concept, workflow-uri, configurare SSH prin Proxmox, Tailscale, troubleshooting - CLAUDE.md simplificat - doar reguli pentru mod non-interactiv - Șters SETUP-COMPLETE.md (conținut mutat în README) - claudep.sh - script pentru task-uri în background cu logging - work.sh - actualizat - .gitignore - adăugat node_modules, Python, IDE, build outputs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
813 lines
34 KiB
Bash
Executable File
813 lines
34 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================================
|
||
# Claude Workflow Script - Lansare Claude automat cu monitorizare progres
|
||
# ============================================================================
|
||
# Funcționalități:
|
||
# - Selectare proiect din workspace
|
||
# - Mod feature (worktree) vs. mod temporar (fără worktree)
|
||
# - Lansare Claude cu -p (non-interactiv) și stream-json pentru progres
|
||
# - Monitorizare în timp real a progresului
|
||
# ============================================================================
|
||
|
||
set -e
|
||
|
||
WORKSPACE="/workspace"
|
||
WORKTREES_BASE="/workspace/.worktrees"
|
||
LOG_DIR="/workspace/.claude-logs"
|
||
|
||
# Inițializare NVM pentru Node
|
||
[ -f ~/.nvm/nvm.sh ] && source ~/.nvm/nvm.sh
|
||
|
||
# Culori pentru output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
MAGENTA='\033[0;35m'
|
||
NC='\033[0m' # No Color
|
||
BOLD='\033[1m'
|
||
|
||
# ============================================================================
|
||
# Funcții utilitare
|
||
# ============================================================================
|
||
|
||
print_header() {
|
||
echo -e "\n${BOLD}${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||
echo -e "${BOLD}${CYAN} $1${NC}"
|
||
echo -e "${BOLD}${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
|
||
}
|
||
|
||
print_section() {
|
||
echo -e "\n${BOLD}${MAGENTA}▸ $1${NC}\n"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✓ $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}✗ $1${NC}"
|
||
}
|
||
|
||
print_info() {
|
||
echo -e "${YELLOW}ℹ $1${NC}"
|
||
}
|
||
|
||
# ============================================================================
|
||
# Selectare Proiect
|
||
# ============================================================================
|
||
|
||
select_project() {
|
||
print_section "Proiecte disponibile"
|
||
|
||
projects=()
|
||
i=1
|
||
for dir in "$WORKSPACE"/*/; do
|
||
if [ -d "$dir/.git" ]; then
|
||
name=$(basename "$dir")
|
||
projects+=("$dir")
|
||
branch=$(cd "$dir" && git branch --show-current 2>/dev/null || echo "n/a")
|
||
echo -e " ${BOLD}$i)${NC} $name ${CYAN}[$branch]${NC}"
|
||
((i++))
|
||
fi
|
||
done
|
||
|
||
if [ ${#projects[@]} -eq 0 ]; then
|
||
print_error "Nu am găsit proiecte Git în $WORKSPACE"
|
||
exit 1
|
||
fi
|
||
|
||
echo ""
|
||
read -p "Alege proiect [1-$((i-1))]: " proj_choice
|
||
|
||
if [ -z "$proj_choice" ] || [ "$proj_choice" -lt 1 ] || [ "$proj_choice" -gt ${#projects[@]} ]; then
|
||
print_error "Selecție invalidă"
|
||
exit 1
|
||
fi
|
||
|
||
PROJECT_DIR="${projects[$((proj_choice-1))]}"
|
||
PROJECT_NAME=$(basename "$PROJECT_DIR")
|
||
|
||
print_success "Proiect selectat: $PROJECT_NAME"
|
||
}
|
||
|
||
# ============================================================================
|
||
# Selectare Mod de Lucru
|
||
# ============================================================================
|
||
|
||
select_work_mode() {
|
||
print_section "Mod de lucru"
|
||
|
||
echo -e " ${BOLD}1)${NC} ${GREEN}Feature nou${NC} ${CYAN}(worktree separat)${NC}"
|
||
echo -e " └─ Creează branch + worktree izolat pentru dezvoltare"
|
||
echo ""
|
||
echo -e " ${BOLD}2)${NC} ${YELLOW}Task temporar${NC} ${CYAN}(background, fără worktree)${NC}"
|
||
echo -e " └─ Rulează în background, revine la meniu"
|
||
echo ""
|
||
echo -e " ${BOLD}3)${NC} ${BLUE}Mod interactiv${NC} ${CYAN}(claude normal)${NC}"
|
||
echo -e " └─ Pornește Claude interactiv pe branch-ul curent"
|
||
echo ""
|
||
echo -e " ${BOLD}4)${NC} ${MAGENTA}Gestionare worktrees${NC}"
|
||
echo -e " └─ Vezi, șterge worktrees existente"
|
||
echo ""
|
||
echo -e " ${BOLD}5)${NC} ${CYAN}Vezi task-uri active${NC}"
|
||
echo -e " └─ Log-uri recente și procese Claude active"
|
||
echo ""
|
||
echo -e " ${BOLD}b)${NC} Înapoi la selectare proiect"
|
||
echo -e " ${BOLD}q)${NC} ${RED}Ieșire${NC}"
|
||
echo ""
|
||
|
||
read -p "Alege mod [1-5/b/q]: " mode_choice
|
||
|
||
case $mode_choice in
|
||
1) WORK_MODE="feature" ;;
|
||
2) WORK_MODE="temp" ;;
|
||
3) WORK_MODE="interactive" ;;
|
||
4) WORK_MODE="manage" ;;
|
||
5) WORK_MODE="status" ;;
|
||
b|B) WORK_MODE="back" ;;
|
||
q|Q)
|
||
echo ""
|
||
print_info "La revedere!"
|
||
exit 0
|
||
;;
|
||
*)
|
||
print_error "Opțiune invalidă"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# ============================================================================
|
||
# Selectare tip feature (pentru modul feature)
|
||
# ============================================================================
|
||
|
||
select_feature_type() {
|
||
print_section "Tip feature"
|
||
|
||
echo -e " ${BOLD}1)${NC} feature/ - Funcționalitate nouă"
|
||
echo -e " ${BOLD}2)${NC} fix/ - Bug fix"
|
||
echo -e " ${BOLD}3)${NC} refactor/ - Refactorizare cod"
|
||
echo -e " ${BOLD}4)${NC} docs/ - Documentație"
|
||
echo -e " ${BOLD}5)${NC} test/ - Teste"
|
||
echo -e " ${BOLD}6)${NC} chore/ - Mentenanță"
|
||
echo -e " ${BOLD}7)${NC} [custom] - Prefix personalizat"
|
||
echo ""
|
||
|
||
read -p "Alege tip [1-7]: " type_choice
|
||
|
||
case $type_choice in
|
||
1) BRANCH_PREFIX="feature" ;;
|
||
2) BRANCH_PREFIX="fix" ;;
|
||
3) BRANCH_PREFIX="refactor" ;;
|
||
4) BRANCH_PREFIX="docs" ;;
|
||
5) BRANCH_PREFIX="test" ;;
|
||
6) BRANCH_PREFIX="chore" ;;
|
||
7)
|
||
read -p "Prefix custom: " BRANCH_PREFIX
|
||
;;
|
||
*) BRANCH_PREFIX="feature" ;;
|
||
esac
|
||
}
|
||
|
||
# ============================================================================
|
||
# Obține descrierea task-ului
|
||
# ============================================================================
|
||
|
||
get_task_description() {
|
||
print_section "Descriere task"
|
||
|
||
echo -e "${CYAN}Descrie ce vrei să facă Claude:${NC}"
|
||
echo ""
|
||
read -p "> " TASK_DESCRIPTION
|
||
|
||
if [ -z "$TASK_DESCRIPTION" ]; then
|
||
print_error "Trebuie să dai o descriere"
|
||
exit 1
|
||
fi
|
||
|
||
# Întreabă dacă vrea să adauge mai multe detalii
|
||
echo ""
|
||
read -p "Detalii adiționale (sau Enter pentru a continua): " extra
|
||
if [ -n "$extra" ]; then
|
||
TASK_DESCRIPTION="$TASK_DESCRIPTION. $extra"
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Creează worktree pentru feature
|
||
# ============================================================================
|
||
|
||
create_worktree() {
|
||
local branch_name="$1"
|
||
local worktree_path="$WORKTREES_BASE/$PROJECT_NAME/$branch_name"
|
||
|
||
mkdir -p "$WORKTREES_BASE/$PROJECT_NAME"
|
||
|
||
cd "$PROJECT_DIR"
|
||
|
||
# Actualizează main/master
|
||
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
|
||
|
||
print_info "Actualizez $DEFAULT_BRANCH..."
|
||
git fetch origin "$DEFAULT_BRANCH" 2>/dev/null || true
|
||
|
||
# Creează worktree
|
||
print_info "Creez worktree: $worktree_path"
|
||
|
||
if [ -d "$worktree_path" ]; then
|
||
print_error "Worktree există deja: $worktree_path"
|
||
read -p "Șterge și recreează? [y/N]: " confirm
|
||
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
|
||
git worktree remove "$worktree_path" --force 2>/dev/null || rm -rf "$worktree_path"
|
||
git branch -D "$branch_name" 2>/dev/null || true
|
||
else
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
git worktree add -b "$branch_name" "$worktree_path" "origin/$DEFAULT_BRANCH"
|
||
|
||
WORKING_DIR="$worktree_path"
|
||
print_success "Worktree creat: $worktree_path"
|
||
}
|
||
|
||
# ============================================================================
|
||
# Rulează Claude în background cu log procesat
|
||
# ============================================================================
|
||
|
||
run_claude_background() {
|
||
local work_dir="$1"
|
||
local prompt="$2"
|
||
local log_file="$3"
|
||
|
||
# Log procesat (mesaje utile, nu JSON brut)
|
||
local processed_log="${log_file%.log}_progress.log"
|
||
# Fișier PID pentru tracking
|
||
local pid_file="${log_file%.log}.pid"
|
||
|
||
mkdir -p "$LOG_DIR"
|
||
|
||
print_section "Lansare Claude în background"
|
||
echo -e "${CYAN}Director:${NC} $work_dir"
|
||
echo -e "${CYAN}Prompt:${NC} $prompt"
|
||
echo ""
|
||
|
||
# Salvează prompt-ul într-un fișier temporar
|
||
local prompt_file=$(mktemp)
|
||
echo "$prompt" > "$prompt_file"
|
||
|
||
# Inițializează log-ul procesat
|
||
{
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo " Claude Code - Task Background"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo "Start: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "Director: $work_dir"
|
||
echo "Prompt: $prompt"
|
||
echo "PID File: $pid_file"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo ""
|
||
} > "$processed_log"
|
||
|
||
# Rulează Claude în background cu procesare a log-ului
|
||
(
|
||
# Salvează PID-ul subshell-ului
|
||
echo "$$" > "$pid_file"
|
||
|
||
cd "$work_dir"
|
||
local start_time=$(date +%s)
|
||
|
||
claude -p "$(cat "$prompt_file")" \
|
||
--output-format stream-json \
|
||
--verbose \
|
||
--allowedTools "Bash(git:*),Bash(npm:*),Bash(node:*),Bash(python3:*),Bash(pip3:*),Bash(pip:*),Bash(ls:*),Bash(cat:*),Bash(mkdir:*),Bash(rm:*),Bash(cp:*),Bash(mv:*),Bash(chmod:*),Bash(touch:*),Bash(find:*),Bash(grep:*),Bash(head:*),Bash(tail:*),Bash(wc:*),Bash(which:*),Bash(stat:*),Bash(file:*),Bash(pgrep:*),Bash(kill:*),Read,Write,Edit,Glob,Grep,WebSearch,WebFetch,Task,Skill,TodoWrite,LSP" \
|
||
--max-turns 50 2>&1 | while IFS= read -r line; do
|
||
|
||
# Parsează JSON și extrage informații relevante
|
||
if echo "$line" | jq -e . >/dev/null 2>&1; then
|
||
msg_type=$(echo "$line" | jq -r '.type // empty' 2>/dev/null)
|
||
timestamp=$(date '+%H:%M:%S')
|
||
|
||
case "$msg_type" in
|
||
"system")
|
||
subtype=$(echo "$line" | jq -r '.subtype // empty' 2>/dev/null)
|
||
if [ "$subtype" = "init" ]; then
|
||
echo "[$timestamp] ▶ Sesiune Claude pornită" >> "$processed_log"
|
||
fi
|
||
;;
|
||
"assistant")
|
||
# Verifică pentru tool_use - extrage detalii complete
|
||
tool_info=$(echo "$line" | jq -r '
|
||
.message.content[]? | select(.type == "tool_use") |
|
||
{name: .name, input: .input} |
|
||
if .name == "WebFetch" then "WebFetch: \(.input.url // "?") | \(.input.prompt // "" | .[0:50])"
|
||
elif .name == "WebSearch" then "WebSearch: \(.input.query // "?")"
|
||
elif .name == "Bash" then "Bash: \(.input.command // "" | .[0:100])"
|
||
elif .name == "Read" then "Read: \(.input.file_path // "?")"
|
||
elif .name == "Write" then "Write: \(.input.file_path // "?")"
|
||
elif .name == "Edit" then "Edit: \(.input.file_path // "?")"
|
||
elif .name == "Grep" then "Grep: \(.input.pattern // "?") in \(.input.path // ".")"
|
||
elif .name == "Glob" then "Glob: \(.input.pattern // "?")"
|
||
elif .name == "Task" then "Task: \(.input.description // "?") [\(.input.subagent_type // "?")]"
|
||
elif .name == "Skill" then "Skill: \(.input.skill // "?")"
|
||
else "\(.name): \(.input | tostring | .[0:80])"
|
||
end
|
||
' 2>/dev/null | head -1)
|
||
if [ -n "$tool_info" ]; then
|
||
echo "[$timestamp] ⚙ $tool_info" >> "$processed_log"
|
||
fi
|
||
|
||
# Text output
|
||
content=$(echo "$line" | jq -r '.message.content[]? | select(.type == "text") | .text // empty' 2>/dev/null | head -c 300)
|
||
if [ -n "$content" ]; then
|
||
echo "[$timestamp] 💬 $content" >> "$processed_log"
|
||
fi
|
||
;;
|
||
"user")
|
||
# Tool result - extrage detalii despre eroare sau rezumat
|
||
tool_result=$(echo "$line" | jq -r '
|
||
.message.content[]? | select(.type == "tool_result") |
|
||
if .is_error == true then
|
||
"✗ EROARE: \(.content // "" | .[0:150])"
|
||
else
|
||
"✓ OK"
|
||
end
|
||
' 2>/dev/null | head -1)
|
||
if [ -n "$tool_result" ]; then
|
||
echo "[$timestamp] $tool_result" >> "$processed_log"
|
||
else
|
||
# Fallback pentru alte formate de rezultat
|
||
is_error=$(echo "$line" | jq -r '.message.content[]?.is_error // false' 2>/dev/null | head -1)
|
||
if [ "$is_error" = "true" ]; then
|
||
err_content=$(echo "$line" | jq -r '.message.content[]?.content // "" | .[0:150]' 2>/dev/null | head -1)
|
||
echo "[$timestamp] ✗ EROARE: $err_content" >> "$processed_log"
|
||
else
|
||
echo "[$timestamp] ✓ OK" >> "$processed_log"
|
||
fi
|
||
fi
|
||
;;
|
||
"result")
|
||
result_text=$(echo "$line" | jq -r '.result // empty' 2>/dev/null | head -c 500)
|
||
echo "" >> "$processed_log"
|
||
echo "[$timestamp] ════════════════════════════════════════════" >> "$processed_log"
|
||
echo "[$timestamp] ✅ TASK FINALIZAT" >> "$processed_log"
|
||
echo "[$timestamp] ════════════════════════════════════════════" >> "$processed_log"
|
||
echo "" >> "$processed_log"
|
||
echo "Rezultat:" >> "$processed_log"
|
||
echo "$result_text" >> "$processed_log"
|
||
;;
|
||
esac
|
||
fi
|
||
done
|
||
|
||
local exit_code=${PIPESTATUS[0]}
|
||
local end_time=$(date +%s)
|
||
local duration=$((end_time - start_time))
|
||
|
||
echo "" >> "$processed_log"
|
||
echo "═══════════════════════════════════════════════════════════════" >> "$processed_log"
|
||
echo "End: $(date '+%Y-%m-%d %H:%M:%S')" >> "$processed_log"
|
||
echo "Durată: ${duration}s" >> "$processed_log"
|
||
echo "Exit code: $exit_code" >> "$processed_log"
|
||
echo "═══════════════════════════════════════════════════════════════" >> "$processed_log"
|
||
|
||
rm -f "$prompt_file"
|
||
rm -f "$pid_file"
|
||
) &
|
||
|
||
local bg_pid=$!
|
||
# Salvează și PID-ul procesului părinte pentru referință
|
||
echo "$bg_pid" > "$pid_file"
|
||
|
||
echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${BOLD}${BLUE}║${NC} ${GREEN}Claude rulează în background${NC} ${BOLD}${BLUE}║${NC}"
|
||
echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
echo -e "${CYAN}PID:${NC} $bg_pid"
|
||
echo -e "${CYAN}Log progres:${NC} $processed_log"
|
||
echo ""
|
||
echo -e "${YELLOW}Comenzi utile:${NC}"
|
||
echo -e " ${BOLD}tail -f $processed_log${NC} # Urmărește progresul"
|
||
echo -e " ${BOLD}ps aux | grep $bg_pid${NC} # Verifică dacă rulează"
|
||
echo ""
|
||
print_success "Revenire la meniu..."
|
||
sleep 2
|
||
}
|
||
|
||
# ============================================================================
|
||
# Rulează Claude cu monitorizare progres (pentru mod feature - sincron)
|
||
# ============================================================================
|
||
|
||
run_claude_with_progress() {
|
||
local work_dir="$1"
|
||
local prompt="$2"
|
||
local log_file="$3"
|
||
|
||
# Log procesat (mesaje utile, nu JSON brut)
|
||
local processed_log="${log_file%.log}_progress.log"
|
||
|
||
cd "$work_dir"
|
||
|
||
print_section "Lansare Claude"
|
||
echo -e "${CYAN}Director:${NC} $work_dir"
|
||
echo -e "${CYAN}Log progres:${NC} $processed_log"
|
||
echo -e "${CYAN}Prompt:${NC} $prompt"
|
||
echo ""
|
||
|
||
# Pregătește log directory
|
||
mkdir -p "$LOG_DIR"
|
||
|
||
local start_time=$(date +%s)
|
||
|
||
# Header pentru progress
|
||
echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${BOLD}${BLUE}║${NC} ${CYAN}Claude Code - Execuție în curs${NC} ${BOLD}${BLUE}║${NC}"
|
||
echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
# Inițializează log-ul procesat
|
||
{
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo " Claude Code - Task"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo "Start: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "Director: $work_dir"
|
||
echo "Prompt: $prompt"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo ""
|
||
} > "$processed_log"
|
||
|
||
# Salvează prompt-ul într-un fișier temporar pentru a evita probleme cu escape
|
||
local prompt_file=$(mktemp)
|
||
echo "$prompt" > "$prompt_file"
|
||
|
||
# Rulează Claude cu stream-json și procesează output-ul în timp real
|
||
claude -p "$(cat "$prompt_file")" \
|
||
--output-format stream-json \
|
||
--verbose \
|
||
--allowedTools "Bash(git:*),Bash(npm:*),Bash(node:*),Bash(python3:*),Bash(pip3:*),Bash(pip:*),Bash(ls:*),Bash(cat:*),Bash(mkdir:*),Bash(rm:*),Bash(cp:*),Bash(mv:*),Bash(chmod:*),Bash(touch:*),Bash(find:*),Bash(grep:*),Bash(head:*),Bash(tail:*),Bash(wc:*),Bash(which:*),Bash(stat:*),Bash(file:*),Bash(pgrep:*),Bash(kill:*),Read,Write,Edit,Glob,Grep,WebSearch,WebFetch,Task,Skill,TodoWrite,LSP" \
|
||
--max-turns 50 2>&1 | while IFS= read -r line; do
|
||
|
||
# Parsează JSON și extrage informații relevante
|
||
if echo "$line" | jq -e . >/dev/null 2>&1; then
|
||
msg_type=$(echo "$line" | jq -r '.type // empty' 2>/dev/null)
|
||
timestamp=$(date '+%H:%M:%S')
|
||
|
||
case "$msg_type" in
|
||
"system")
|
||
subtype=$(echo "$line" | jq -r '.subtype // empty' 2>/dev/null)
|
||
if [ "$subtype" = "init" ]; then
|
||
echo "[$timestamp] ▶ Sesiune Claude pornită" >> "$processed_log"
|
||
echo -e "${GREEN}▶${NC} Sesiune Claude pornită"
|
||
fi
|
||
;;
|
||
"assistant")
|
||
# Verifică pentru tool_use - extrage detalii complete
|
||
tool_info=$(echo "$line" | jq -r '
|
||
.message.content[]? | select(.type == "tool_use") |
|
||
{name: .name, input: .input} |
|
||
if .name == "WebFetch" then "WebFetch: \(.input.url // "?") | \(.input.prompt // "" | .[0:50])"
|
||
elif .name == "WebSearch" then "WebSearch: \(.input.query // "?")"
|
||
elif .name == "Bash" then "Bash: \(.input.command // "" | .[0:100])"
|
||
elif .name == "Read" then "Read: \(.input.file_path // "?")"
|
||
elif .name == "Write" then "Write: \(.input.file_path // "?")"
|
||
elif .name == "Edit" then "Edit: \(.input.file_path // "?")"
|
||
elif .name == "Grep" then "Grep: \(.input.pattern // "?") in \(.input.path // ".")"
|
||
elif .name == "Glob" then "Glob: \(.input.pattern // "?")"
|
||
elif .name == "Task" then "Task: \(.input.description // "?") [\(.input.subagent_type // "?")]"
|
||
elif .name == "Skill" then "Skill: \(.input.skill // "?")"
|
||
else "\(.name): \(.input | tostring | .[0:80])"
|
||
end
|
||
' 2>/dev/null | head -1)
|
||
if [ -n "$tool_info" ]; then
|
||
echo "[$timestamp] ⚙ $tool_info" >> "$processed_log"
|
||
echo -e "${YELLOW}⚙${NC} $tool_info"
|
||
fi
|
||
|
||
# Text output
|
||
content=$(echo "$line" | jq -r '.message.content[]? | select(.type == "text") | .text // empty' 2>/dev/null | head -c 300)
|
||
if [ -n "$content" ]; then
|
||
echo "[$timestamp] 💬 $content" >> "$processed_log"
|
||
echo -e "${GREEN}◉${NC} $content"
|
||
fi
|
||
;;
|
||
"user")
|
||
# Tool result - extrage detalii despre eroare
|
||
tool_result=$(echo "$line" | jq -r '
|
||
.message.content[]? | select(.type == "tool_result") |
|
||
if .is_error == true then
|
||
"✗ EROARE: \(.content // "" | .[0:150])"
|
||
else
|
||
"✓ OK"
|
||
end
|
||
' 2>/dev/null | head -1)
|
||
if [ -n "$tool_result" ]; then
|
||
echo "[$timestamp] $tool_result" >> "$processed_log"
|
||
if [[ "$tool_result" == *"EROARE"* ]]; then
|
||
echo -e "${RED} $tool_result${NC}"
|
||
else
|
||
echo -e "${CYAN} $tool_result${NC}"
|
||
fi
|
||
else
|
||
is_error=$(echo "$line" | jq -r '.message.content[]?.is_error // false' 2>/dev/null | head -1)
|
||
if [ "$is_error" = "true" ]; then
|
||
err_content=$(echo "$line" | jq -r '.message.content[]?.content // "" | .[0:150]' 2>/dev/null | head -1)
|
||
echo "[$timestamp] ✗ EROARE: $err_content" >> "$processed_log"
|
||
echo -e "${RED} ✗ EROARE: $err_content${NC}"
|
||
else
|
||
echo "[$timestamp] ✓ OK" >> "$processed_log"
|
||
echo -e "${CYAN} ✓ OK${NC}"
|
||
fi
|
||
fi
|
||
;;
|
||
"result")
|
||
result_text=$(echo "$line" | jq -r '.result // empty' 2>/dev/null | head -c 500)
|
||
echo "" >> "$processed_log"
|
||
echo "[$timestamp] ════════════════════════════════════════════" >> "$processed_log"
|
||
echo "[$timestamp] ✅ TASK FINALIZAT" >> "$processed_log"
|
||
echo "[$timestamp] ════════════════════════════════════════════" >> "$processed_log"
|
||
echo "Rezultat:" >> "$processed_log"
|
||
echo "$result_text" >> "$processed_log"
|
||
|
||
echo ""
|
||
echo -e "${GREEN}✓ TASK FINALIZAT${NC}"
|
||
echo -e "$result_text"
|
||
;;
|
||
esac
|
||
fi
|
||
done
|
||
|
||
local exit_code=${PIPESTATUS[0]}
|
||
local end_time=$(date +%s)
|
||
local duration=$((end_time - start_time))
|
||
|
||
rm -f "$prompt_file"
|
||
|
||
# Finalizează log-ul procesat
|
||
{
|
||
echo ""
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
echo "End: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "Durată: ${duration}s"
|
||
echo "Exit code: $exit_code"
|
||
echo "═══════════════════════════════════════════════════════════════"
|
||
} >> "$processed_log"
|
||
|
||
echo ""
|
||
echo -e "${BOLD}${BLUE}════════════════════════════════════════════════════════════════${NC}"
|
||
|
||
if [ $exit_code -eq 0 ]; then
|
||
print_success "Claude a terminat în ${duration}s"
|
||
else
|
||
print_error "Claude a terminat cu eroare (cod: $exit_code) în ${duration}s"
|
||
fi
|
||
|
||
echo -e "${CYAN}Log progres:${NC} $processed_log"
|
||
echo -e "${BOLD}${BLUE}════════════════════════════════════════════════════════════════${NC}"
|
||
|
||
return $exit_code
|
||
}
|
||
|
||
# ============================================================================
|
||
# Vezi task-uri active și log-uri recente
|
||
# ============================================================================
|
||
|
||
show_task_status() {
|
||
print_section "Task-uri active și log-uri recente"
|
||
|
||
# Afișează procese Claude active (bazat pe fișiere PID)
|
||
echo -e "${CYAN}Task-uri background active:${NC}"
|
||
local active_count=0
|
||
shopt -s nullglob
|
||
for pid_file in "$LOG_DIR"/*.pid; do
|
||
[ -f "$pid_file" ] || continue
|
||
local pid=$(cat "$pid_file" 2>/dev/null)
|
||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||
local log_name=$(basename "${pid_file%.pid}_progress.log")
|
||
echo -e " ${GREEN}●${NC} PID: $pid → $log_name"
|
||
((active_count++))
|
||
else
|
||
# PID invalid sau proces terminat - șterge fișierul
|
||
rm -f "$pid_file" 2>/dev/null
|
||
fi
|
||
done
|
||
if [ $active_count -eq 0 ]; then
|
||
echo -e " ${YELLOW}○${NC} Niciun task activ"
|
||
fi
|
||
echo ""
|
||
|
||
# Afișează log-uri de progres recente
|
||
echo -e "${CYAN}Log-uri recente (ultimele 5):${NC}"
|
||
local logs=$(ls -t "$LOG_DIR"/*_progress.log 2>/dev/null | head -5)
|
||
if [ -n "$logs" ]; then
|
||
local i=1
|
||
for log in $logs; do
|
||
local log_basename=$(basename "$log")
|
||
local pid_file="${log%_progress.log}.pid"
|
||
|
||
# Verifică status
|
||
if [ -f "$pid_file" ]; then
|
||
local pid=$(cat "$pid_file" 2>/dev/null)
|
||
if kill -0 "$pid" 2>/dev/null; then
|
||
status="${BLUE}◐ În curs (PID: $pid)${NC}"
|
||
else
|
||
status="${RED}✗ Întrerupt${NC}"
|
||
rm -f "$pid_file" 2>/dev/null
|
||
fi
|
||
elif grep -q "TASK FINALIZAT" "$log" 2>/dev/null; then
|
||
status="${GREEN}✓ Finalizat${NC}"
|
||
elif grep -q "Exit code:" "$log" 2>/dev/null; then
|
||
exit_code=$(grep "Exit code:" "$log" | tail -1 | awk '{print $NF}')
|
||
if [ "$exit_code" = "0" ]; then
|
||
status="${GREEN}✓ Terminat OK${NC}"
|
||
else
|
||
status="${RED}✗ Terminat cu eroare ($exit_code)${NC}"
|
||
fi
|
||
else
|
||
status="${RED}✗ Întrerupt/Incomplet${NC}"
|
||
fi
|
||
|
||
# Afișează ultima activitate
|
||
local last_activity=$(grep "^\[" "$log" 2>/dev/null | tail -1 | head -c 100)
|
||
|
||
echo -e " ${BOLD}$i)${NC} $log_basename"
|
||
echo -e " Status: $status"
|
||
echo -e " Ultima: ${CYAN}${last_activity:-N/A}${NC}"
|
||
echo -e " Path: $log"
|
||
echo ""
|
||
((i++))
|
||
done
|
||
echo -e "${YELLOW}Comenzi utile:${NC}"
|
||
echo -e " tail -f <path> # Urmărește live"
|
||
echo -e " less <path> # Citește complet"
|
||
else
|
||
echo -e " ${YELLOW}○${NC} Nu există log-uri"
|
||
fi
|
||
echo ""
|
||
echo -e " ${BOLD}b)${NC} Înapoi la meniu"
|
||
echo -e " ${BOLD}q)${NC} ${RED}Ieșire${NC}"
|
||
echo ""
|
||
|
||
read -p "Alege [b/q]: " status_choice
|
||
case $status_choice in
|
||
q|Q)
|
||
echo ""
|
||
print_info "La revedere!"
|
||
exit 0
|
||
;;
|
||
*)
|
||
# Orice altceva (inclusiv b sau Enter) - înapoi la meniu
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# ============================================================================
|
||
# Gestionare worktrees
|
||
# ============================================================================
|
||
|
||
manage_worktrees() {
|
||
while true; do
|
||
print_section "Worktrees existente pentru $PROJECT_NAME"
|
||
|
||
cd "$PROJECT_DIR"
|
||
|
||
echo -e "${CYAN}Worktrees:${NC}"
|
||
git worktree list
|
||
echo ""
|
||
|
||
echo -e " ${BOLD}1)${NC} Șterge un worktree"
|
||
echo -e " ${BOLD}2)${NC} Navighează la un worktree"
|
||
echo -e " ${BOLD}b)${NC} Înapoi la meniu"
|
||
echo -e " ${BOLD}q)${NC} ${RED}Ieșire${NC}"
|
||
echo ""
|
||
|
||
read -p "Alege [1-2/b/q]: " manage_choice
|
||
|
||
case $manage_choice in
|
||
1)
|
||
read -p "Path worktree de șters: " wt_path
|
||
if [ -n "$wt_path" ]; then
|
||
git worktree remove "$wt_path" --force
|
||
print_success "Worktree șters"
|
||
fi
|
||
;;
|
||
2)
|
||
read -p "Path worktree: " wt_path
|
||
if [ -d "$wt_path" ]; then
|
||
cd "$wt_path"
|
||
print_success "Navigat la $wt_path"
|
||
exec bash
|
||
fi
|
||
;;
|
||
b|B)
|
||
exec "$0"
|
||
;;
|
||
q|Q)
|
||
echo ""
|
||
print_info "La revedere!"
|
||
exit 0
|
||
;;
|
||
*)
|
||
print_error "Opțiune invalidă"
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# ============================================================================
|
||
# Main
|
||
# ============================================================================
|
||
|
||
main() {
|
||
print_header "Claude Workflow Manager"
|
||
|
||
# 1. Selectare proiect
|
||
select_project
|
||
|
||
# 2. Selectare mod de lucru (cu loop pentru navigare)
|
||
while true; do
|
||
select_work_mode
|
||
|
||
# Dacă vrea înapoi, repornește scriptul
|
||
if [ "$WORK_MODE" = "back" ]; then
|
||
exec "$0"
|
||
fi
|
||
|
||
break
|
||
done
|
||
|
||
case $WORK_MODE in
|
||
"feature")
|
||
# Mod feature cu worktree
|
||
select_feature_type
|
||
|
||
echo ""
|
||
read -p "Nume scurt feature (ex: add-login, fix-auth): " feature_name
|
||
|
||
if [ -z "$feature_name" ]; then
|
||
print_error "Trebuie să dai un nume pentru feature"
|
||
exit 1
|
||
fi
|
||
|
||
BRANCH_NAME="${BRANCH_PREFIX}/${feature_name}"
|
||
|
||
get_task_description
|
||
|
||
# Creează worktree
|
||
create_worktree "$BRANCH_NAME"
|
||
|
||
# Generează log file
|
||
LOG_FILE="$LOG_DIR/${PROJECT_NAME}_${feature_name}_$(date +%Y%m%d_%H%M%S).log"
|
||
|
||
# Rulează Claude
|
||
run_claude_with_progress "$WORKING_DIR" "$TASK_DESCRIPTION" "$LOG_FILE"
|
||
|
||
echo ""
|
||
echo -e "${CYAN}Pentru a continua manual:${NC}"
|
||
echo -e " cd $WORKING_DIR"
|
||
echo -e " claude"
|
||
;;
|
||
|
||
"temp")
|
||
# Mod temporar fără worktree - rulează în background
|
||
get_task_description
|
||
|
||
# Folosește directorul proiectului direct
|
||
WORKING_DIR="$PROJECT_DIR"
|
||
|
||
# Generează log file
|
||
LOG_FILE="$LOG_DIR/${PROJECT_NAME}_temp_$(date +%Y%m%d_%H%M%S).log"
|
||
|
||
# Rulează Claude în background și revine la meniu
|
||
run_claude_background "$WORKING_DIR" "$TASK_DESCRIPTION" "$LOG_FILE"
|
||
|
||
# Revine la meniu
|
||
exec "$0"
|
||
;;
|
||
|
||
"interactive")
|
||
# Mod interactiv normal
|
||
cd "$PROJECT_DIR"
|
||
print_info "Pornesc Claude interactiv în $PROJECT_NAME..."
|
||
exec claude
|
||
;;
|
||
|
||
"manage")
|
||
# Gestionare worktrees
|
||
manage_worktrees
|
||
;;
|
||
|
||
"status")
|
||
# Vezi task-uri active
|
||
show_task_status
|
||
# Revine la meniu
|
||
exec "$0"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Rulează main
|
||
main "$@"
|