loki-mode 6.67.3 → 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 +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +156 -35
- package/autonomy/run.sh +2 -0
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/web-app/server.py +55 -18
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.
|
|
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.
|
|
271
|
+
**v6.68.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.68.0
|
package/autonomy/loki
CHANGED
|
@@ -7054,70 +7054,191 @@ QPRDEOF
|
|
|
7054
7054
|
cmd_monitor() {
|
|
7055
7055
|
local project_dir="${1:-.}"
|
|
7056
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
|
+
|
|
7057
7065
|
if [[ ! -f "$project_dir/docker-compose.yml" ]] && [[ ! -f "$project_dir/docker-compose.yaml" ]]; then
|
|
7058
7066
|
echo -e "${RED}No docker-compose.yml found in $project_dir${NC}"
|
|
7059
7067
|
return 1
|
|
7060
7068
|
fi
|
|
7061
7069
|
|
|
7062
|
-
echo -e "${CYAN}
|
|
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}"
|
|
7063
7073
|
echo "Press Ctrl+C to stop."
|
|
7064
7074
|
echo ""
|
|
7065
7075
|
|
|
7066
7076
|
local fix_count=0
|
|
7067
|
-
local max_fixes=10
|
|
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
|
|
7068
7082
|
|
|
7069
7083
|
while true; do
|
|
7070
|
-
#
|
|
7071
|
-
local
|
|
7072
|
-
|
|
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)
|
|
7073
7087
|
|
|
7074
|
-
if [[ -z "$
|
|
7088
|
+
if [[ -z "$ps_output" ]]; then
|
|
7075
7089
|
echo -e "${YELLOW}No services found. Waiting...${NC}"
|
|
7076
|
-
sleep
|
|
7090
|
+
sleep "$poll_interval"
|
|
7077
7091
|
continue
|
|
7078
7092
|
fi
|
|
7079
7093
|
|
|
7080
|
-
# Parse
|
|
7081
|
-
local
|
|
7094
|
+
# Parse services and find failures
|
|
7095
|
+
local failed_services=""
|
|
7096
|
+
local all_healthy=true
|
|
7097
|
+
local service_summary=""
|
|
7098
|
+
|
|
7082
7099
|
while IFS= read -r line; do
|
|
7083
7100
|
[[ -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)
|
|
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} "
|
|
7087
7107
|
|
|
7088
7108
|
if [[ "$svc_state" == "exited" ]] || [[ "$svc_state" == "dead" ]]; then
|
|
7089
|
-
|
|
7090
|
-
|
|
7109
|
+
all_healthy=false
|
|
7110
|
+
failed_services="${failed_services} ${svc_name}"
|
|
7111
|
+
fi
|
|
7112
|
+
done <<< "$ps_output"
|
|
7091
7113
|
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
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
|
|
7096
7120
|
|
|
7097
|
-
|
|
7121
|
+
consecutive_healthy=0
|
|
7098
7122
|
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
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
|
|
7103
7131
|
|
|
7104
|
-
|
|
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
|
|
7132
|
+
echo -e "\n${RED}[FAILURE] $svc_name is down${NC}"
|
|
7107
7133
|
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
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
|
|
7112
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"
|
|
7113
7171
|
fi
|
|
7114
|
-
done <<< "$status"
|
|
7115
7172
|
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
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
|
|
7119
7240
|
|
|
7120
|
-
sleep
|
|
7241
|
+
sleep "$poll_interval"
|
|
7121
7242
|
done
|
|
7122
7243
|
}
|
|
7123
7244
|
|
package/autonomy/run.sh
CHANGED
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
package/web-app/server.py
CHANGED
|
@@ -1481,7 +1481,7 @@ class DevServerManager:
|
|
|
1481
1481
|
if framework == "docker":
|
|
1482
1482
|
await asyncio.to_thread(
|
|
1483
1483
|
subprocess.run,
|
|
1484
|
-
["docker", "compose", "up", "-d", "--build"],
|
|
1484
|
+
["docker", "compose", "up", "-d", "--build", "--no-deps"],
|
|
1485
1485
|
capture_output=True, cwd=str(target), timeout=120
|
|
1486
1486
|
)
|
|
1487
1487
|
# Restart the dev server
|
|
@@ -1732,17 +1732,17 @@ class DevServerManager:
|
|
|
1732
1732
|
was_running = prev.get("status") in ("running", None)
|
|
1733
1733
|
now_failed = svc["state"] in ("exited", "dead")
|
|
1734
1734
|
|
|
1735
|
-
#
|
|
1736
|
-
#
|
|
1737
|
-
#
|
|
1738
|
-
#
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
and
|
|
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
1743
|
)
|
|
1744
1744
|
|
|
1745
|
-
if (was_running and now_failed) or
|
|
1745
|
+
if (was_running and now_failed) or persistently_failed:
|
|
1746
1746
|
svc_health["restarts"] = prev.get("restarts", 0) + 1
|
|
1747
1747
|
logger.warning("Docker service '%s' failed (exit %s)", name, svc.get("exit_code"))
|
|
1748
1748
|
|
|
@@ -1766,14 +1766,51 @@ class DevServerManager:
|
|
|
1766
1766
|
|
|
1767
1767
|
info["_auto_fixing"] = True
|
|
1768
1768
|
svc_logs = docker_ctx.get("service_logs", {}).get(name, "")
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
if
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1769
|
+
|
|
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"""
|
|
1777
1814
|
|
|
1778
1815
|
svc_health["fix_attempts"] += 1
|
|
1779
1816
|
svc_health["fix_timestamps"] = recent_fixes + [now]
|