loki-mode 6.67.1 → 6.67.3

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.1
6
+ # Loki Mode v6.67.3
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.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
271
+ **v6.67.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.67.1
1
+ 6.67.3
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,77 @@ 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
+ if [[ ! -f "$project_dir/docker-compose.yml" ]] && [[ ! -f "$project_dir/docker-compose.yaml" ]]; then
7058
+ echo -e "${RED}No docker-compose.yml found in $project_dir${NC}"
7059
+ return 1
7060
+ fi
7061
+
7062
+ echo -e "${CYAN}Monitoring Docker Compose services in $project_dir...${NC}"
7063
+ echo "Press Ctrl+C to stop."
7064
+ echo ""
7065
+
7066
+ local fix_count=0
7067
+ local max_fixes=10
7068
+
7069
+ while true; do
7070
+ # Check service status
7071
+ local status
7072
+ status=$(cd "$project_dir" && docker compose ps -a --format json 2>/dev/null)
7073
+
7074
+ if [[ -z "$status" ]]; then
7075
+ echo -e "${YELLOW}No services found. Waiting...${NC}"
7076
+ sleep 10
7077
+ continue
7078
+ fi
7079
+
7080
+ # Parse each service
7081
+ local has_failure=false
7082
+ while IFS= read -r line; do
7083
+ [[ -z "$line" ]] && continue
7084
+ local svc_name svc_state
7085
+ 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)
7086
+ svc_state=$(echo "$line" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('State',''))" 2>/dev/null)
7087
+
7088
+ if [[ "$svc_state" == "exited" ]] || [[ "$svc_state" == "dead" ]]; then
7089
+ has_failure=true
7090
+ echo -e "${RED}Service '$svc_name' is $svc_state${NC}"
7091
+
7092
+ if [[ $fix_count -lt $max_fixes ]]; then
7093
+ # Get logs for the failed service
7094
+ local logs
7095
+ logs=$(cd "$project_dir" && docker compose logs --tail 30 "$svc_name" 2>/dev/null)
7096
+
7097
+ echo -e "${CYAN}Auto-fixing $svc_name...${NC}"
7098
+
7099
+ # Run loki quick with targeted fix
7100
+ LOKI_MAX_ITERATIONS=5 LOKI_AUTO_FIX=true \
7101
+ loki quick "Fix the '$svc_name' Docker service. It exited with an error. Logs: $logs" \
7102
+ 2>&1 | while IFS= read -r fline; do echo " $fline"; done
7103
+
7104
+ # Rebuild and restart
7105
+ echo -e "${CYAN}Rebuilding $svc_name...${NC}"
7106
+ (cd "$project_dir" && docker compose up -d --build --no-deps "$svc_name" 2>&1) | while IFS= read -r fline; do echo " $fline"; done
7107
+
7108
+ fix_count=$((fix_count + 1))
7109
+ echo -e "${GREEN}Fix attempt $fix_count/$max_fixes for $svc_name${NC}"
7110
+ else
7111
+ echo -e "${YELLOW}Max fix attempts ($max_fixes) reached. Manual intervention needed.${NC}"
7112
+ fi
7113
+ fi
7114
+ done <<< "$status"
7115
+
7116
+ if [[ "$has_failure" == "false" ]]; then
7117
+ echo -e "\r${GREEN}All services healthy${NC} $(date +%H:%M:%S) \c"
7118
+ fi
7119
+
7120
+ sleep 10
7121
+ done
7122
+ }
7123
+
7051
7124
  # Project scaffolding (v6.28.0)
7052
7125
  cmd_init() {
7053
7126
  # Guard: check if .loki/ already exists to avoid overwriting active session
@@ -10487,6 +10560,9 @@ main() {
10487
10560
  quick)
10488
10561
  cmd_quick "$@"
10489
10562
  ;;
10563
+ monitor)
10564
+ cmd_monitor "$@"
10565
+ ;;
10490
10566
  demo)
10491
10567
  cmd_demo "$@"
10492
10568
  ;;
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
 
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.1"
10
+ __version__ = "6.67.3"
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.1
5
+ **Version:** v6.67.3
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.1'
60
+ __version__ = '6.67.3'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.67.1",
3
+ "version": "6.67.3",
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"],
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
+ # Also detect services that never started (failed on initial docker compose up).
1736
+ # On first poll, prev is empty ({}) so prev.get("status") is None, which counts
1737
+ # as "was_running" above. But if a service was already "exited" before we ever
1738
+ # saw it as "running", the next poll would see prev.status="exited" and skip it.
1739
+ # Detect this case: first time we see a service in a failed state with no prior fix.
1740
+ first_seen_failed = (
1741
+ not prev # No previous health record for this service
1742
+ and now_failed
1743
+ )
1744
+
1745
+ if (was_running and now_failed) or first_seen_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
 
@@ -1794,24 +1813,70 @@ class DevServerManager:
1794
1813
  return
1795
1814
 
1796
1815
  try:
1816
+ fix_env = {**os.environ, **_load_secrets()}
1817
+ fix_env["LOKI_MAX_ITERATIONS"] = "5" # More iterations for complex Docker fixes
1818
+
1797
1819
  proc = await asyncio.to_thread(
1798
1820
  subprocess.run,
1799
1821
  [loki, "quick", fix_prompt],
1800
1822
  capture_output=True, text=True, cwd=project_dir, timeout=300,
1801
- env={**os.environ, **_load_secrets()}
1823
+ env=fix_env
1802
1824
  )
1803
1825
 
1804
- # After fix, restart the specific service
1826
+ # Rebuild the image (fix may have changed Dockerfile or package.json)
1827
+ # --no-deps prevents restarting healthy services, --build rebuilds with the fix
1805
1828
  await asyncio.to_thread(
1806
1829
  subprocess.run,
1807
- ["docker", "compose", "restart", service_name],
1808
- capture_output=True, cwd=project_dir, timeout=30
1830
+ ["docker", "compose", "up", "-d", "--build", "--no-deps", service_name],
1831
+ capture_output=True, cwd=project_dir, timeout=120
1809
1832
  )
1810
1833
 
1834
+ # Wait for service to stabilize after rebuild
1835
+ await asyncio.sleep(10)
1836
+
1837
+ # Verify the fix actually worked
1838
+ fix_worked = False
1839
+ try:
1840
+ verify_proc = await asyncio.to_thread(
1841
+ subprocess.run,
1842
+ ["docker", "compose", "ps", "-a", "--format", "json"],
1843
+ capture_output=True, text=True, cwd=project_dir, timeout=10
1844
+ )
1845
+ if verify_proc.returncode == 0 and verify_proc.stdout.strip():
1846
+ raw = verify_proc.stdout.strip()
1847
+ try:
1848
+ parsed = json.loads(raw)
1849
+ if not isinstance(parsed, list):
1850
+ parsed = [parsed]
1851
+ except json.JSONDecodeError:
1852
+ parsed = []
1853
+ for line in raw.split("\n"):
1854
+ if line.strip():
1855
+ try:
1856
+ parsed.append(json.loads(line))
1857
+ except json.JSONDecodeError:
1858
+ pass
1859
+ for svc in parsed:
1860
+ if isinstance(svc, dict) and svc.get("Name", svc.get("name", "")) == service_name:
1861
+ state = svc.get("State", svc.get("state", ""))
1862
+ fix_worked = state == "running"
1863
+ break
1864
+ except Exception:
1865
+ logger.debug("Post-fix verification failed for service '%s'", service_name, exc_info=True)
1866
+
1867
+ # Determine final fix status
1868
+ if proc.returncode == 0 and fix_worked:
1869
+ final_status = "fixed"
1870
+ elif proc.returncode == 0 and not fix_worked:
1871
+ final_status = "fix_failed"
1872
+ logger.warning("loki quick succeeded but service '%s' still not running", service_name)
1873
+ else:
1874
+ final_status = "fix_failed"
1875
+
1811
1876
  # BUG-V64-005: Re-fetch from live info dict to avoid writing to detached dict
1812
1877
  info = self.servers.get(session_id)
1813
1878
  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"
1879
+ info["docker_service_health"][service_name]["fix_status"] = final_status
1815
1880
  except Exception as exc:
1816
1881
  logger.error("Auto-fix for service '%s' failed: %s", service_name, exc)
1817
1882
  # BUG-V64-005: Re-fetch from live info dict
@@ -1856,7 +1921,7 @@ async def _gather_docker_context(project_dir: Path) -> dict:
1856
1921
  # Get service status via docker compose ps
1857
1922
  try:
1858
1923
  ps_proc = await loop.run_in_executor(None, lambda: subprocess.run(
1859
- ["docker", "compose", "ps", "--format", "json"],
1924
+ ["docker", "compose", "ps", "-a", "--format", "json"],
1860
1925
  capture_output=True, text=True, cwd=str(project_dir), timeout=10
1861
1926
  ))
1862
1927
  if ps_proc.returncode == 0 and ps_proc.stdout.strip():
@@ -1975,6 +2040,12 @@ def _diagnose_errors(logs: str) -> list[dict]:
1975
2040
  (r"npm ERR!|npm error",
1976
2041
  lambda m: {"pattern": "npm_error", "diagnosis": "npm encountered an error",
1977
2042
  "suggestion": "Check package.json for invalid dependencies or run 'npm install' manually."}),
2043
+ (r"ENOTEMPTY.*node_modules|rename.*node_modules.*ENOTEMPTY",
2044
+ lambda m: {"pattern": "node_modules_volume_conflict", "diagnosis": "Docker volume conflict with node_modules",
2045
+ "suggestion": "Replace named volume with anonymous volume in docker-compose.yml: use '- /app/node_modules' instead of '- name:/app/node_modules'. Then run 'docker compose down -v && docker compose up --build'."}),
2046
+ (r"EACCES.*permission denied|EPERM.*operation not permitted",
2047
+ lambda m: {"pattern": "permission_denied", "diagnosis": "File permission error in container",
2048
+ "suggestion": "Check Dockerfile USER directive and volume mount permissions. May need 'chown' in Dockerfile."}),
1978
2049
  ]
1979
2050
  seen: set[str] = set()
1980
2051
  for pattern, handler in patterns: