loki-mode 6.67.2 → 6.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v6.67.2
6
+ # Loki Mode v6.68.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -268,4 +268,4 @@ The following features are documented in skill modules but not yet fully automat
268
268
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
269
269
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
270
270
 
271
- **v6.67.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
271
+ **v6.68.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.67.2
1
+ 6.68.0
package/autonomy/loki CHANGED
@@ -398,6 +398,7 @@ show_help() {
398
398
  echo " run <issue> Issue-driven engineering (v6.0.0) - GitHub/GitLab/Jira/Azure DevOps"
399
399
  echo " start [PRD] Start Loki Mode (optionally with PRD file)"
400
400
  echo " quick \"task\" Quick single-task mode (lightweight, 3 iterations max)"
401
+ echo " monitor [path] Monitor Docker Compose services with auto-fix (v6.67.0)"
401
402
  echo " demo Run interactive demo (~60s simulated session)"
402
403
  echo " init [name] Project scaffolding with 22 PRD templates"
403
404
  echo " issue <url|num> [DEPRECATED] Use 'loki run' instead"
@@ -6987,11 +6988,12 @@ cmd_quick() {
6987
6988
 
6988
6989
  local task_desc="$*"
6989
6990
  local version=$(get_version)
6991
+ local max_iter="${LOKI_MAX_ITERATIONS:-3}"
6990
6992
 
6991
6993
  echo -e "${BOLD}Loki Mode v$version - Quick Mode${NC}"
6992
6994
  echo ""
6993
6995
  echo -e "${CYAN}Task:${NC} $task_desc"
6994
- echo -e "${DIM}Running lightweight execution (3 iterations max, no quality council)${NC}"
6996
+ echo -e "${DIM}Running lightweight execution ($max_iter iterations max, no quality council)${NC}"
6995
6997
  echo ""
6996
6998
 
6997
6999
  # Create quick PRD from task description
@@ -7028,7 +7030,7 @@ QPRDEOF
7028
7030
  echo ""
7029
7031
 
7030
7032
  # Set lightweight execution environment
7031
- export LOKI_MAX_ITERATIONS=3
7033
+ export LOKI_MAX_ITERATIONS="$max_iter"
7032
7034
  export LOKI_COMPLEXITY=simple
7033
7035
  export LOKI_COUNCIL_ENABLED=false
7034
7036
  export LOKI_PHASE_CODE_REVIEW=false
@@ -7048,6 +7050,198 @@ QPRDEOF
7048
7050
  exec "$RUN_SH" "$quick_prd"
7049
7051
  }
7050
7052
 
7053
+ # Docker Compose monitoring with auto-fix (v6.67.0)
7054
+ cmd_monitor() {
7055
+ local project_dir="${1:-.}"
7056
+
7057
+ # Resolve to absolute path
7058
+ if [[ ! "$project_dir" = /* ]]; then
7059
+ project_dir="$(cd "$project_dir" 2>/dev/null && pwd)" || {
7060
+ echo -e "${RED}Error: directory not found: $1${NC}"
7061
+ return 1
7062
+ }
7063
+ fi
7064
+
7065
+ if [[ ! -f "$project_dir/docker-compose.yml" ]] && [[ ! -f "$project_dir/docker-compose.yaml" ]]; then
7066
+ echo -e "${RED}No docker-compose.yml found in $project_dir${NC}"
7067
+ return 1
7068
+ fi
7069
+
7070
+ echo -e "${CYAN}AI-Powered Docker Monitor${NC}"
7071
+ echo -e "Project: ${BOLD}$project_dir${NC}"
7072
+ echo -e "Provider: ${GREEN}${LOKI_PROVIDER:-claude}${NC}"
7073
+ echo "Press Ctrl+C to stop."
7074
+ echo ""
7075
+
7076
+ local fix_count=0
7077
+ local max_fixes="${LOKI_MONITOR_MAX_FIXES:-10}"
7078
+ local poll_interval="${LOKI_MONITOR_INTERVAL:-10}"
7079
+ local consecutive_healthy=0
7080
+
7081
+ trap 'echo ""; echo -e "${CYAN}Monitor stopped.${NC}"; return 0' INT TERM
7082
+
7083
+ while true; do
7084
+ # 1. CAPTURE: Get service status (all services including stopped)
7085
+ local ps_output
7086
+ ps_output=$(cd "$project_dir" && docker compose ps -a --format json 2>/dev/null)
7087
+
7088
+ if [[ -z "$ps_output" ]]; then
7089
+ echo -e "${YELLOW}No services found. Waiting...${NC}"
7090
+ sleep "$poll_interval"
7091
+ continue
7092
+ fi
7093
+
7094
+ # Parse services and find failures
7095
+ local failed_services=""
7096
+ local all_healthy=true
7097
+ local service_summary=""
7098
+
7099
+ while IFS= read -r line; do
7100
+ [[ -z "$line" ]] && continue
7101
+ local svc_name svc_state svc_exit
7102
+ svc_name=$(echo "$line" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('Service',d.get('Name','')))" 2>/dev/null || echo "unknown")
7103
+ svc_state=$(echo "$line" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('State','unknown'))" 2>/dev/null || echo "unknown")
7104
+ svc_exit=$(echo "$line" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('ExitCode',0))" 2>/dev/null || echo "0")
7105
+
7106
+ service_summary="${service_summary}${svc_name}=${svc_state} "
7107
+
7108
+ if [[ "$svc_state" == "exited" ]] || [[ "$svc_state" == "dead" ]]; then
7109
+ all_healthy=false
7110
+ failed_services="${failed_services} ${svc_name}"
7111
+ fi
7112
+ done <<< "$ps_output"
7113
+
7114
+ if [[ "$all_healthy" == "true" ]]; then
7115
+ consecutive_healthy=$((consecutive_healthy + 1))
7116
+ printf "\r${GREEN}[healthy]${NC} %s (%s) " "$service_summary" "$(date +%H:%M:%S)"
7117
+ sleep "$poll_interval"
7118
+ continue
7119
+ fi
7120
+
7121
+ consecutive_healthy=0
7122
+
7123
+ # 2. CAPTURE LOGS and CONTEXT for each failed service, then feed to AI
7124
+ for svc_name in $failed_services; do
7125
+ if [[ $fix_count -ge $max_fixes ]]; then
7126
+ echo -e "\n${RED}Max fix attempts ($max_fixes) reached for this session.${NC}"
7127
+ echo "Manual intervention needed. Check: cd $project_dir && docker compose logs $svc_name"
7128
+ sleep "$poll_interval"
7129
+ continue 2
7130
+ fi
7131
+
7132
+ echo -e "\n${RED}[FAILURE] $svc_name is down${NC}"
7133
+
7134
+ local logs
7135
+ logs=$(cd "$project_dir" && docker compose logs --tail 50 "$svc_name" 2>/dev/null | head -c 3000)
7136
+
7137
+ # Gather docker-compose.yml for AI context (truncated to avoid ARG_MAX)
7138
+ local compose_content=""
7139
+ if [[ -f "$project_dir/docker-compose.yml" ]]; then
7140
+ compose_content=$(head -c 5000 "$project_dir/docker-compose.yml")
7141
+ elif [[ -f "$project_dir/docker-compose.yaml" ]]; then
7142
+ compose_content=$(head -c 5000 "$project_dir/docker-compose.yaml")
7143
+ fi
7144
+
7145
+ # Gather Dockerfile for the failing service (truncated)
7146
+ local dockerfile_content=""
7147
+ for df_path in "$project_dir/$svc_name/Dockerfile" "$project_dir/Dockerfile" "$project_dir/Dockerfile.$svc_name"; do
7148
+ if [[ -f "$df_path" ]]; then
7149
+ dockerfile_content=$(head -c 3000 "$df_path")
7150
+ break
7151
+ fi
7152
+ done
7153
+
7154
+ # 3. FEED TO AI: Construct a rich prompt with all context
7155
+ local ai_prompt="You are debugging a Docker Compose service that has failed.
7156
+
7157
+ SERVICE: $svc_name
7158
+ STATUS: exited/dead
7159
+
7160
+ DOCKER COMPOSE LOGS (last 50 lines):
7161
+ $logs
7162
+
7163
+ DOCKER-COMPOSE.YML:
7164
+ $compose_content"
7165
+
7166
+ if [[ -n "$dockerfile_content" ]]; then
7167
+ ai_prompt="${ai_prompt}
7168
+
7169
+ DOCKERFILE ($svc_name/Dockerfile):
7170
+ $dockerfile_content"
7171
+ fi
7172
+
7173
+ ai_prompt="${ai_prompt}
7174
+
7175
+ INSTRUCTIONS:
7176
+ 1. Analyze the error in the logs above
7177
+ 2. Identify the root cause
7178
+ 3. Fix the issue by editing the necessary files (docker-compose.yml, Dockerfile, source code, package.json, requirements.txt, etc.)
7179
+ 4. Make sure the fix works on any platform (Docker Desktop, Linux, Docker-in-Docker, Kubernetes)
7180
+ 5. Do NOT just restart -- actually fix the underlying code/config problem
7181
+ 6. After fixing, the system will rebuild with 'docker compose up --build'
7182
+ 7. Common issues: named volumes for node_modules (use anonymous), missing dependencies, port conflicts, wrong commands"
7183
+
7184
+ echo -e "${CYAN}[AI] Analyzing $svc_name failure with ${LOKI_PROVIDER:-claude}...${NC}"
7185
+
7186
+ # 4. LET AI FIX: Run loki quick with the AI prompt
7187
+ # The AI provider (claude/codex/gemini/ollama) decides what to fix
7188
+ fix_count=$((fix_count + 1))
7189
+ echo -e "${CYAN}[FIX] Attempt $fix_count/$max_fixes${NC}"
7190
+
7191
+ (
7192
+ cd "$project_dir"
7193
+ LOKI_MAX_ITERATIONS=5 LOKI_AUTO_FIX=true \
7194
+ "$0" quick "$ai_prompt" 2>&1 | while IFS= read -r fline; do
7195
+ echo " $fline"
7196
+ done
7197
+ )
7198
+
7199
+ # 5. REBUILD: Docker compose up --build for the fixed service
7200
+ echo -e "${CYAN}[REBUILD] Rebuilding $svc_name...${NC}"
7201
+ (cd "$project_dir" && docker compose up -d --build --no-deps "$svc_name" 2>&1) | while IFS= read -r fline; do
7202
+ echo " $fline"
7203
+ done
7204
+
7205
+ # 6. VERIFY: Wait and check if the fix worked
7206
+ echo -e "${CYAN}[VERIFY] Waiting 15s for $svc_name to stabilize...${NC}"
7207
+ sleep 15
7208
+
7209
+ local verify_state
7210
+ verify_state=$(cd "$project_dir" && docker compose ps -a --format json 2>/dev/null | VERIFY_SVC="$svc_name" python3 -c "
7211
+ import json, sys, os
7212
+ svc = os.environ['VERIFY_SVC']
7213
+ raw = sys.stdin.read().strip()
7214
+ if not raw:
7215
+ print('unknown')
7216
+ sys.exit(0)
7217
+ # Handle both JSON array (v2.21+) and NDJSON formats
7218
+ try:
7219
+ parsed = json.loads(raw)
7220
+ services = parsed if isinstance(parsed, list) else [parsed]
7221
+ except json.JSONDecodeError:
7222
+ services = []
7223
+ for line in raw.split(chr(10)):
7224
+ try: services.append(json.loads(line))
7225
+ except: pass
7226
+ for s in services:
7227
+ if not isinstance(s, dict): continue
7228
+ if s.get('Service', s.get('Name','')) == svc:
7229
+ print(s.get('State','unknown'))
7230
+ sys.exit(0)
7231
+ print('unknown')
7232
+ " 2>/dev/null || echo "unknown")
7233
+
7234
+ if [[ "$verify_state" == "running" ]]; then
7235
+ echo -e "${GREEN}[SUCCESS] $svc_name is now running!${NC}"
7236
+ else
7237
+ echo -e "${YELLOW}[RETRY] $svc_name still not healthy (state: $verify_state). Will retry on next poll.${NC}"
7238
+ fi
7239
+ done
7240
+
7241
+ sleep "$poll_interval"
7242
+ done
7243
+ }
7244
+
7051
7245
  # Project scaffolding (v6.28.0)
7052
7246
  cmd_init() {
7053
7247
  # Guard: check if .loki/ already exists to avoid overwriting active session
@@ -10487,6 +10681,9 @@ main() {
10487
10681
  quick)
10488
10682
  cmd_quick "$@"
10489
10683
  ;;
10684
+ monitor)
10685
+ cmd_monitor "$@"
10686
+ ;;
10490
10687
  demo)
10491
10688
  cmd_demo "$@"
10492
10689
  ;;
package/autonomy/run.sh CHANGED
@@ -580,6 +580,11 @@ PHASE_UAT=${LOKI_PHASE_UAT:-true}
580
580
  COMPLETION_PROMISE=${LOKI_COMPLETION_PROMISE:-""}
581
581
  MAX_ITERATIONS=${LOKI_MAX_ITERATIONS:-1000}
582
582
  ITERATION_COUNT=0
583
+
584
+ # If this is an auto-fix task, allow more iterations
585
+ if [[ "${LOKI_AUTO_FIX:-}" == "true" ]]; then
586
+ MAX_ITERATIONS="${LOKI_MAX_ITERATIONS:-5}"
587
+ fi
583
588
  # Perpetual mode: never stop unless max iterations (ignores all completion signals)
584
589
  PERPETUAL_MODE=${LOKI_PERPETUAL_MODE:-false}
585
590
 
@@ -625,6 +630,8 @@ if [ -f "$TELEMETRY_SCRIPT" ]; then
625
630
  source "$TELEMETRY_SCRIPT"
626
631
  fi
627
632
 
633
+
634
+
628
635
  # 2026 Research Enhancements (minimal additions)
629
636
  PROMPT_REPETITION=${LOKI_PROMPT_REPETITION:-true}
630
637
  CONFIDENCE_ROUTING=${LOKI_CONFIDENCE_ROUTING:-true}
package/completions/_loki CHANGED
@@ -91,6 +91,9 @@ function _loki {
91
91
  completions)
92
92
  _arguments '1:shell:(bash zsh)'
93
93
  ;;
94
+ monitor)
95
+ _directories
96
+ ;;
94
97
  context|ctx)
95
98
  _loki_context
96
99
  ;;
@@ -107,6 +110,7 @@ function _loki_commands {
107
110
  commands=(
108
111
  'start:Start Loki Mode'
109
112
  'quick:Quick single-task mode'
113
+ 'monitor:Monitor Docker Compose services with auto-fix'
110
114
  'demo:Interactive 60-second demo'
111
115
  'init:Interactive PRD builder'
112
116
  'stop:Stop execution'
@@ -5,7 +5,7 @@ _loki_completion() {
5
5
  _init_completion || return
6
6
 
7
7
  # Main subcommands (must match autonomy/loki main case statement)
8
- local main_commands="start quick demo init stop pause resume status dashboard logs serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share explain plan report test ci watch telemetry agent context code run export review optimize heal migrate cluster worktree trigger failover remote version completions help"
8
+ local main_commands="start quick monitor demo init stop pause resume status dashboard logs serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share explain plan report test ci watch telemetry agent context code run export review optimize heal migrate cluster worktree trigger failover remote version completions help"
9
9
 
10
10
  # 1. If we are on the first argument (subcommand)
11
11
  if [[ $cword -eq 1 ]]; then
@@ -178,6 +178,11 @@ _loki_completion() {
178
178
  fi
179
179
  ;;
180
180
 
181
+ monitor)
182
+ # Complete with directories
183
+ _filedir -d
184
+ ;;
185
+
181
186
  completions)
182
187
  COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
183
188
  ;;
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.67.2"
10
+ __version__ = "6.68.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.67.2
5
+ **Version:** v6.68.0
6
6
 
7
7
  ---
8
8
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.67.2'
60
+ __version__ = '6.68.0'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.67.2",
3
+ "version": "6.68.0",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",
package/web-app/server.py CHANGED
@@ -1454,9 +1454,10 @@ class DevServerManager:
1454
1454
  except Exception:
1455
1455
  logger.debug("Docker context gathering for auto-fix failed", exc_info=True)
1456
1456
 
1457
- # Save original command and multi_service flag before stop() removes the info dict
1457
+ # Save original command, framework, and multi_service flag before stop() removes the info dict
1458
1458
  cmd = info.get("original_command")
1459
1459
  is_multi_service = info.get("multi_service", False)
1460
+ framework = info.get("framework")
1460
1461
 
1461
1462
  try:
1462
1463
  auto_fix_env = {**os.environ}
@@ -1475,6 +1476,14 @@ class DevServerManager:
1475
1476
  )
1476
1477
  if result.returncode == 0:
1477
1478
  logger.info("Auto-fix succeeded for session %s, restarting dev server", session_id)
1479
+ # For Docker projects, rebuild images before restarting
1480
+ # (fix may have changed Dockerfile, package.json, requirements.txt, etc.)
1481
+ if framework == "docker":
1482
+ await asyncio.to_thread(
1483
+ subprocess.run,
1484
+ ["docker", "compose", "up", "-d", "--build", "--no-deps"],
1485
+ capture_output=True, cwd=str(target), timeout=120
1486
+ )
1478
1487
  # Restart the dev server
1479
1488
  await self.stop(session_id)
1480
1489
  await asyncio.sleep(1)
@@ -1723,7 +1732,17 @@ class DevServerManager:
1723
1732
  was_running = prev.get("status") in ("running", None)
1724
1733
  now_failed = svc["state"] in ("exited", "dead")
1725
1734
 
1726
- if was_running and now_failed:
1735
+ # Detect services that need fixing:
1736
+ # 1. was_running and now_failed: service transitioned from running to exited
1737
+ # 2. Persistently failed: service is exited AND has never been successfully fixed
1738
+ # (fix_attempts == 0 or fix_status != "fixed")
1739
+ persistently_failed = (
1740
+ now_failed
1741
+ and prev.get("fix_status") != "fixed"
1742
+ and prev.get("status") in ("exited", "dead", None)
1743
+ )
1744
+
1745
+ if (was_running and now_failed) or persistently_failed:
1727
1746
  svc_health["restarts"] = prev.get("restarts", 0) + 1
1728
1747
  logger.warning("Docker service '%s' failed (exit %s)", name, svc.get("exit_code"))
1729
1748
 
@@ -1747,14 +1766,51 @@ class DevServerManager:
1747
1766
 
1748
1767
  info["_auto_fixing"] = True
1749
1768
  svc_logs = docker_ctx.get("service_logs", {}).get(name, "")
1750
- diagnoses = _diagnose_errors(svc_logs)
1751
- diag_text = "\n".join(f"- {d['diagnosis']}: {d['suggestion']}" for d in diagnoses)
1752
1769
 
1753
- fix_prompt = f"The '{name}' Docker service crashed (exit code {svc.get('exit_code', 1)}).\n"
1754
- if diag_text:
1755
- fix_prompt += f"\nDiagnosis:\n{diag_text}\n"
1756
- fix_prompt += f"\nService logs:\n{svc_logs[:2000]}\n"
1757
- fix_prompt += "\nFix the issue and ensure the service starts correctly."
1770
+ # Read compose file for AI context
1771
+ compose_content = ""
1772
+ compose_file = project_dir / "docker-compose.yml"
1773
+ if not compose_file.exists():
1774
+ compose_file = project_dir / "docker-compose.yaml"
1775
+ if compose_file.exists():
1776
+ try:
1777
+ compose_content = compose_file.read_text(errors="replace")[:5000]
1778
+ except OSError:
1779
+ pass
1780
+
1781
+ # Read service Dockerfile for AI context
1782
+ dockerfile_content = ""
1783
+ for df_path in [project_dir / name / "Dockerfile", project_dir / "Dockerfile"]:
1784
+ if df_path.exists():
1785
+ try:
1786
+ dockerfile_content = df_path.read_text(errors="replace")[:3000]
1787
+ break
1788
+ except OSError:
1789
+ pass
1790
+
1791
+ fix_prompt = f"""You are debugging a Docker Compose service that has failed.
1792
+
1793
+ SERVICE: {name}
1794
+ STATUS: exited/dead (exit code {svc.get('exit_code', 1)})
1795
+
1796
+ DOCKER COMPOSE LOGS (last 50 lines):
1797
+ {svc_logs[:3000]}
1798
+
1799
+ DOCKER-COMPOSE.YML:
1800
+ {compose_content}
1801
+ """
1802
+ if dockerfile_content:
1803
+ fix_prompt += f"\nDOCKERFILE ({name}/Dockerfile):\n{dockerfile_content}\n"
1804
+
1805
+ fix_prompt += """
1806
+ INSTRUCTIONS:
1807
+ 1. Analyze the error in the logs above
1808
+ 2. Identify the root cause
1809
+ 3. Fix the issue by editing the necessary files (docker-compose.yml, Dockerfile, source code, package.json, requirements.txt, etc.)
1810
+ 4. Make sure the fix works on any platform (Docker Desktop, Linux, Docker-in-Docker, Kubernetes)
1811
+ 5. Do NOT just restart -- actually fix the underlying code/config problem
1812
+ 6. After fixing, the system will rebuild with 'docker compose up --build'
1813
+ 7. Common issues: named volumes for node_modules (use anonymous), missing dependencies, port conflicts, wrong commands"""
1758
1814
 
1759
1815
  svc_health["fix_attempts"] += 1
1760
1816
  svc_health["fix_timestamps"] = recent_fixes + [now]
@@ -1794,24 +1850,70 @@ class DevServerManager:
1794
1850
  return
1795
1851
 
1796
1852
  try:
1853
+ fix_env = {**os.environ, **_load_secrets()}
1854
+ fix_env["LOKI_MAX_ITERATIONS"] = "5" # More iterations for complex Docker fixes
1855
+
1797
1856
  proc = await asyncio.to_thread(
1798
1857
  subprocess.run,
1799
1858
  [loki, "quick", fix_prompt],
1800
1859
  capture_output=True, text=True, cwd=project_dir, timeout=300,
1801
- env={**os.environ, **_load_secrets()}
1860
+ env=fix_env
1802
1861
  )
1803
1862
 
1804
- # After fix, restart the specific service
1863
+ # Rebuild the image (fix may have changed Dockerfile or package.json)
1864
+ # --no-deps prevents restarting healthy services, --build rebuilds with the fix
1805
1865
  await asyncio.to_thread(
1806
1866
  subprocess.run,
1807
- ["docker", "compose", "restart", service_name],
1808
- capture_output=True, cwd=project_dir, timeout=30
1867
+ ["docker", "compose", "up", "-d", "--build", "--no-deps", service_name],
1868
+ capture_output=True, cwd=project_dir, timeout=120
1809
1869
  )
1810
1870
 
1871
+ # Wait for service to stabilize after rebuild
1872
+ await asyncio.sleep(10)
1873
+
1874
+ # Verify the fix actually worked
1875
+ fix_worked = False
1876
+ try:
1877
+ verify_proc = await asyncio.to_thread(
1878
+ subprocess.run,
1879
+ ["docker", "compose", "ps", "-a", "--format", "json"],
1880
+ capture_output=True, text=True, cwd=project_dir, timeout=10
1881
+ )
1882
+ if verify_proc.returncode == 0 and verify_proc.stdout.strip():
1883
+ raw = verify_proc.stdout.strip()
1884
+ try:
1885
+ parsed = json.loads(raw)
1886
+ if not isinstance(parsed, list):
1887
+ parsed = [parsed]
1888
+ except json.JSONDecodeError:
1889
+ parsed = []
1890
+ for line in raw.split("\n"):
1891
+ if line.strip():
1892
+ try:
1893
+ parsed.append(json.loads(line))
1894
+ except json.JSONDecodeError:
1895
+ pass
1896
+ for svc in parsed:
1897
+ if isinstance(svc, dict) and svc.get("Name", svc.get("name", "")) == service_name:
1898
+ state = svc.get("State", svc.get("state", ""))
1899
+ fix_worked = state == "running"
1900
+ break
1901
+ except Exception:
1902
+ logger.debug("Post-fix verification failed for service '%s'", service_name, exc_info=True)
1903
+
1904
+ # Determine final fix status
1905
+ if proc.returncode == 0 and fix_worked:
1906
+ final_status = "fixed"
1907
+ elif proc.returncode == 0 and not fix_worked:
1908
+ final_status = "fix_failed"
1909
+ logger.warning("loki quick succeeded but service '%s' still not running", service_name)
1910
+ else:
1911
+ final_status = "fix_failed"
1912
+
1811
1913
  # BUG-V64-005: Re-fetch from live info dict to avoid writing to detached dict
1812
1914
  info = self.servers.get(session_id)
1813
1915
  if info and "docker_service_health" in info and service_name in info["docker_service_health"]:
1814
- info["docker_service_health"][service_name]["fix_status"] = "fixed" if proc.returncode == 0 else "fix_failed"
1916
+ info["docker_service_health"][service_name]["fix_status"] = final_status
1815
1917
  except Exception as exc:
1816
1918
  logger.error("Auto-fix for service '%s' failed: %s", service_name, exc)
1817
1919
  # BUG-V64-005: Re-fetch from live info dict
@@ -1856,7 +1958,7 @@ async def _gather_docker_context(project_dir: Path) -> dict:
1856
1958
  # Get service status via docker compose ps
1857
1959
  try:
1858
1960
  ps_proc = await loop.run_in_executor(None, lambda: subprocess.run(
1859
- ["docker", "compose", "ps", "--format", "json"],
1961
+ ["docker", "compose", "ps", "-a", "--format", "json"],
1860
1962
  capture_output=True, text=True, cwd=str(project_dir), timeout=10
1861
1963
  ))
1862
1964
  if ps_proc.returncode == 0 and ps_proc.stdout.strip():