loki-mode 5.26.2 → 5.27.1
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/completion-council.sh +19 -18
- package/autonomy/hooks/quality-gate.sh +1 -1
- package/autonomy/hooks/track-metrics.sh +2 -1
- package/autonomy/hooks/validate-bash.sh +6 -5
- package/autonomy/loki +20 -13
- package/autonomy/run.sh +43 -11
- package/dashboard/__init__.py +1 -1
- package/dashboard/control.py +2 -2
- package/dashboard/server.py +268 -27
- package/dashboard/static/index.html +243 -141
- package/docs/INSTALLATION.md +1 -1
- package/events/emit.sh +5 -4
- package/learning/__init__.py +157 -0
- package/learning/aggregate.sh +251 -0
- package/learning/aggregator.py +1107 -0
- package/learning/emit.sh +520 -0
- package/learning/emitter.py +554 -0
- package/learning/signals.py +597 -0
- package/learning/signals.ts +759 -0
- package/learning/suggest.sh +298 -0
- package/learning/suggestions.py +839 -0
- package/memory/engine.py +4 -4
- package/memory/retrieval.py +29 -24
- package/memory/storage.py +9 -2
- package/memory/vector_index.py +9 -5
- package/package.json +2 -1
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 zero human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v5.
|
|
6
|
+
# Loki Mode v5.27.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -260,4 +260,4 @@ Auto-detected or force with `LOKI_COMPLEXITY`:
|
|
|
260
260
|
|
|
261
261
|
---
|
|
262
262
|
|
|
263
|
-
**v5.
|
|
263
|
+
**v5.27.1 | Unified dashboard data flow, overview component, backend fixes | ~270 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.27.1
|
|
@@ -226,6 +226,7 @@ council_vote() {
|
|
|
226
226
|
1) role="requirements_verifier" ;;
|
|
227
227
|
2) role="test_auditor" ;;
|
|
228
228
|
3) role="devils_advocate" ;;
|
|
229
|
+
*) role="generalist" ;;
|
|
229
230
|
esac
|
|
230
231
|
|
|
231
232
|
log_info "Council member $member/$COUNCIL_SIZE ($role) reviewing..."
|
|
@@ -255,7 +256,24 @@ council_vote() {
|
|
|
255
256
|
((member++))
|
|
256
257
|
done
|
|
257
258
|
|
|
258
|
-
#
|
|
259
|
+
# Anti-sycophancy check: if unanimous APPROVE, run devil's advocate
|
|
260
|
+
if [ $approve_count -eq $COUNCIL_SIZE ] && [ $COUNCIL_SIZE -ge 3 ]; then
|
|
261
|
+
log_warn "Unanimous approval detected - running anti-sycophancy check..."
|
|
262
|
+
local contrarian_verdict
|
|
263
|
+
contrarian_verdict=$(council_devils_advocate "$evidence_file" "$vote_dir")
|
|
264
|
+
local contrarian_vote
|
|
265
|
+
contrarian_vote=$(echo "$contrarian_verdict" | grep -oE "VOTE:\s*(APPROVE|REJECT)" | grep -oE "APPROVE|REJECT" | head -1)
|
|
266
|
+
|
|
267
|
+
if [ "$contrarian_vote" = "REJECT" ]; then
|
|
268
|
+
log_warn "Anti-sycophancy: Devil's advocate REJECTED unanimous approval"
|
|
269
|
+
log_warn "Overriding to require one more iteration for verification"
|
|
270
|
+
approve_count=$((approve_count - 1))
|
|
271
|
+
else
|
|
272
|
+
log_info "Anti-sycophancy: Devil's advocate confirmed approval"
|
|
273
|
+
fi
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# Record vote results (AFTER anti-sycophancy check so verdict reflects any override)
|
|
259
277
|
_COUNCIL_STATE_FILE="$COUNCIL_STATE_DIR/state.json" \
|
|
260
278
|
_COUNCIL_SIZE="$COUNCIL_SIZE" \
|
|
261
279
|
_COUNCIL_APPROVE="$approve_count" \
|
|
@@ -291,23 +309,6 @@ with open(state_file, 'w') as f:
|
|
|
291
309
|
json.dump(state, f, indent=2)
|
|
292
310
|
" || log_warn "Failed to record council vote results"
|
|
293
311
|
|
|
294
|
-
# Anti-sycophancy check: if unanimous APPROVE, run devil's advocate
|
|
295
|
-
if [ $approve_count -eq $COUNCIL_SIZE ] && [ $COUNCIL_SIZE -ge 3 ]; then
|
|
296
|
-
log_warn "Unanimous approval detected - running anti-sycophancy check..."
|
|
297
|
-
local contrarian_verdict
|
|
298
|
-
contrarian_verdict=$(council_devils_advocate "$evidence_file" "$vote_dir")
|
|
299
|
-
local contrarian_vote
|
|
300
|
-
contrarian_vote=$(echo "$contrarian_verdict" | grep -oE "VOTE:\s*(APPROVE|REJECT)" | grep -oE "APPROVE|REJECT" | head -1)
|
|
301
|
-
|
|
302
|
-
if [ "$contrarian_vote" = "REJECT" ]; then
|
|
303
|
-
log_warn "Anti-sycophancy: Devil's advocate REJECTED unanimous approval"
|
|
304
|
-
log_warn "Overriding to require one more iteration for verification"
|
|
305
|
-
approve_count=$((approve_count - 1))
|
|
306
|
-
else
|
|
307
|
-
log_info "Anti-sycophancy: Devil's advocate confirmed approval"
|
|
308
|
-
fi
|
|
309
|
-
fi
|
|
310
|
-
|
|
311
312
|
echo ""
|
|
312
313
|
log_info "Council verdict: $approve_count APPROVE / $reject_count REJECT (threshold: $COUNCIL_THRESHOLD)"
|
|
313
314
|
echo -e "$verdicts"
|
|
@@ -29,7 +29,7 @@ fi
|
|
|
29
29
|
|
|
30
30
|
# Check for TODO/FIXME in recent changes
|
|
31
31
|
if [ -d "$CWD/.git" ]; then
|
|
32
|
-
TODOS=$(git -C "$CWD" diff HEAD~1 2>/dev/null | grep -c "TODO\|FIXME" ||
|
|
32
|
+
TODOS=$(git -C "$CWD" diff HEAD~1 2>/dev/null | grep -c "TODO\|FIXME" 2>/dev/null) || TODOS=0
|
|
33
33
|
if [ "$TODOS" -gt 0 ]; then
|
|
34
34
|
GATE_RESULTS+=("new_todos: $TODOS")
|
|
35
35
|
fi
|
|
@@ -11,6 +11,7 @@ METRICS_DIR="$CWD/.loki/metrics"
|
|
|
11
11
|
mkdir -p "$METRICS_DIR"
|
|
12
12
|
|
|
13
13
|
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
14
|
-
|
|
14
|
+
tool_escaped=$(printf '%s' "$TOOL_NAME" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
15
|
+
echo "{\"timestamp\":\"$TIMESTAMP\",\"tool\":\"$tool_escaped\",\"event\":\"PostToolUse\"}" >> "$METRICS_DIR/tool-usage.jsonl"
|
|
15
16
|
|
|
16
17
|
exit 0
|
|
@@ -10,13 +10,13 @@ CWD=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).ge
|
|
|
10
10
|
|
|
11
11
|
# Dangerous command patterns
|
|
12
12
|
BLOCKED_PATTERNS=(
|
|
13
|
-
"^rm -rf
|
|
14
|
-
"^rm -rf
|
|
15
|
-
"^rm -rf \\\$HOME
|
|
13
|
+
"^rm -rf /"
|
|
14
|
+
"^rm -rf ~"
|
|
15
|
+
"^rm -rf \\\$HOME"
|
|
16
16
|
"> /dev/sd"
|
|
17
17
|
"^mkfs "
|
|
18
18
|
"^dd if=/dev/zero"
|
|
19
|
-
"^chmod -R 777
|
|
19
|
+
"^chmod -R 777 /"
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
# Check for blocked patterns
|
|
@@ -38,7 +38,8 @@ done
|
|
|
38
38
|
# Log command to audit trail
|
|
39
39
|
LOG_DIR="$CWD/.loki/logs"
|
|
40
40
|
mkdir -p "$LOG_DIR"
|
|
41
|
-
|
|
41
|
+
printf '%s' "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"command\":$(echo "$COMMAND" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}" >> "$LOG_DIR/bash-audit.jsonl"
|
|
42
|
+
echo >> "$LOG_DIR/bash-audit.jsonl"
|
|
42
43
|
|
|
43
44
|
# Allow command
|
|
44
45
|
cat << EOF
|
package/autonomy/loki
CHANGED
|
@@ -942,7 +942,7 @@ cmd_provider_set() {
|
|
|
942
942
|
echo "Install: npm install -g @openai/codex"
|
|
943
943
|
;;
|
|
944
944
|
gemini)
|
|
945
|
-
echo "Install: npm install -g @
|
|
945
|
+
echo "Install: npm install -g @google/gemini-cli"
|
|
946
946
|
;;
|
|
947
947
|
esac
|
|
948
948
|
echo ""
|
|
@@ -1026,7 +1026,7 @@ cmd_provider_info() {
|
|
|
1026
1026
|
echo "Name: Codex CLI"
|
|
1027
1027
|
echo "Vendor: OpenAI"
|
|
1028
1028
|
echo "CLI: codex"
|
|
1029
|
-
echo "Flag:
|
|
1029
|
+
echo "Flag: --full-auto"
|
|
1030
1030
|
echo ""
|
|
1031
1031
|
echo "Features:"
|
|
1032
1032
|
echo " - Autonomous mode"
|
|
@@ -1038,7 +1038,7 @@ cmd_provider_info() {
|
|
|
1038
1038
|
echo "Name: Gemini CLI"
|
|
1039
1039
|
echo "Vendor: Google"
|
|
1040
1040
|
echo "CLI: gemini"
|
|
1041
|
-
echo "Flag: --yolo"
|
|
1041
|
+
echo "Flag: --approval-mode=yolo"
|
|
1042
1042
|
echo ""
|
|
1043
1043
|
echo "Features:"
|
|
1044
1044
|
echo " - Autonomous mode"
|
|
@@ -1572,7 +1572,7 @@ cmd_issue_parse() {
|
|
|
1572
1572
|
;;
|
|
1573
1573
|
--format)
|
|
1574
1574
|
format="${2:-yaml}"
|
|
1575
|
-
shift 2
|
|
1575
|
+
if [ $# -ge 2 ]; then shift 2; else shift; fi
|
|
1576
1576
|
;;
|
|
1577
1577
|
--format=*)
|
|
1578
1578
|
format="${1#*=}"
|
|
@@ -1580,7 +1580,7 @@ cmd_issue_parse() {
|
|
|
1580
1580
|
;;
|
|
1581
1581
|
--output|-o)
|
|
1582
1582
|
output_file="${2:-}"
|
|
1583
|
-
shift 2
|
|
1583
|
+
if [ $# -ge 2 ]; then shift 2; else shift; fi
|
|
1584
1584
|
;;
|
|
1585
1585
|
--output=*)
|
|
1586
1586
|
output_file="${1#*=}"
|
|
@@ -2291,7 +2291,7 @@ cmd_api() {
|
|
|
2291
2291
|
fi
|
|
2292
2292
|
|
|
2293
2293
|
# Start server
|
|
2294
|
-
mkdir -p "$LOKI_DIR"
|
|
2294
|
+
mkdir -p "$LOKI_DIR/logs"
|
|
2295
2295
|
nohup node "$api_server" --port "$port" > "$LOKI_DIR/logs/api.log" 2>&1 &
|
|
2296
2296
|
local new_pid=$!
|
|
2297
2297
|
echo "$new_pid" > "$pid_file"
|
|
@@ -2612,6 +2612,8 @@ send_slack_notification() {
|
|
|
2612
2612
|
project_name=$(basename "$(pwd)")
|
|
2613
2613
|
local timestamp
|
|
2614
2614
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
2615
|
+
local message_escaped
|
|
2616
|
+
message_escaped=$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g')
|
|
2615
2617
|
|
|
2616
2618
|
# Build Slack payload with blocks for better formatting
|
|
2617
2619
|
local payload
|
|
@@ -2629,7 +2631,7 @@ send_slack_notification() {
|
|
|
2629
2631
|
"type": "section",
|
|
2630
2632
|
"text": {
|
|
2631
2633
|
"type": "mrkdwn",
|
|
2632
|
-
"text": "$
|
|
2634
|
+
"text": "$message_escaped"
|
|
2633
2635
|
}
|
|
2634
2636
|
},
|
|
2635
2637
|
{
|
|
@@ -2666,6 +2668,8 @@ send_discord_notification() {
|
|
|
2666
2668
|
project_name=$(basename "$(pwd)")
|
|
2667
2669
|
local timestamp
|
|
2668
2670
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
2671
|
+
local message_escaped
|
|
2672
|
+
message_escaped=$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g')
|
|
2669
2673
|
|
|
2670
2674
|
# Build Discord embed payload
|
|
2671
2675
|
local payload
|
|
@@ -2674,7 +2678,7 @@ send_discord_notification() {
|
|
|
2674
2678
|
"embeds": [
|
|
2675
2679
|
{
|
|
2676
2680
|
"title": "Loki Mode: $event_type",
|
|
2677
|
-
"description": "$
|
|
2681
|
+
"description": "$message_escaped",
|
|
2678
2682
|
"color": 5814783,
|
|
2679
2683
|
"footer": {
|
|
2680
2684
|
"text": "Project: $project_name | $timestamp"
|
|
@@ -2705,6 +2709,8 @@ send_webhook_notification() {
|
|
|
2705
2709
|
project_name=$(basename "$(pwd)")
|
|
2706
2710
|
local timestamp
|
|
2707
2711
|
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
2712
|
+
local message_escaped
|
|
2713
|
+
message_escaped=$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g')
|
|
2708
2714
|
|
|
2709
2715
|
# Build generic JSON payload
|
|
2710
2716
|
local payload
|
|
@@ -2712,7 +2718,7 @@ send_webhook_notification() {
|
|
|
2712
2718
|
{
|
|
2713
2719
|
"source": "loki-mode",
|
|
2714
2720
|
"event": "$event_type",
|
|
2715
|
-
"message": "$
|
|
2721
|
+
"message": "$message_escaped",
|
|
2716
2722
|
"project": "$project_name",
|
|
2717
2723
|
"timestamp": "$timestamp",
|
|
2718
2724
|
"cwd": "$(pwd)"
|
|
@@ -3085,11 +3091,11 @@ if count == 0:
|
|
|
3085
3091
|
;;
|
|
3086
3092
|
|
|
3087
3093
|
all)
|
|
3088
|
-
cmd_memory show patterns --limit "$limit"
|
|
3094
|
+
cmd_memory show patterns --limit "$limit" --project "$project"
|
|
3089
3095
|
echo ""
|
|
3090
|
-
cmd_memory show mistakes --limit "$limit"
|
|
3096
|
+
cmd_memory show mistakes --limit "$limit" --project "$project"
|
|
3091
3097
|
echo ""
|
|
3092
|
-
cmd_memory show successes --limit "$limit"
|
|
3098
|
+
cmd_memory show successes --limit "$limit" --project "$project"
|
|
3093
3099
|
;;
|
|
3094
3100
|
|
|
3095
3101
|
*)
|
|
@@ -3110,12 +3116,13 @@ if count == 0:
|
|
|
3110
3116
|
echo -e "${BOLD}Search Results for: $query${NC}"
|
|
3111
3117
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
3112
3118
|
|
|
3119
|
+
local query_escaped="${query//\'/\'\\\'\'}"
|
|
3113
3120
|
python3 -c "
|
|
3114
3121
|
import json
|
|
3115
3122
|
import os
|
|
3116
3123
|
|
|
3117
3124
|
learnings_dir = '$learnings_dir'
|
|
3118
|
-
query = '$
|
|
3125
|
+
query = '${query_escaped}'.lower()
|
|
3119
3126
|
|
|
3120
3127
|
for filename in ['patterns.jsonl', 'mistakes.jsonl', 'successes.jsonl']:
|
|
3121
3128
|
filepath = os.path.join(learnings_dir, filename)
|
package/autonomy/run.sh
CHANGED
|
@@ -582,6 +582,8 @@ else
|
|
|
582
582
|
# shellcheck disable=SC2178
|
|
583
583
|
WORKTREE_PATHS=""
|
|
584
584
|
fi
|
|
585
|
+
# Track background install PIDs for cleanup (indexed array, works on all bash versions)
|
|
586
|
+
WORKTREE_INSTALL_PIDS=()
|
|
585
587
|
|
|
586
588
|
# Colors
|
|
587
589
|
RED='\033[0;31m'
|
|
@@ -1052,9 +1054,15 @@ import_github_issues() {
|
|
|
1052
1054
|
local pending_file=".loki/queue/pending.json"
|
|
1053
1055
|
local task_count=0
|
|
1054
1056
|
|
|
1055
|
-
# Ensure pending.json exists
|
|
1057
|
+
# Ensure pending.json exists with correct format for GitHub import
|
|
1056
1058
|
if [ ! -f "$pending_file" ]; then
|
|
1057
1059
|
echo '{"tasks":[]}' > "$pending_file"
|
|
1060
|
+
elif jq -e 'type == "array"' "$pending_file" &>/dev/null; then
|
|
1061
|
+
# Normalize bare array format to {"tasks":[...]} for GitHub import compatibility
|
|
1062
|
+
local _tmp_normalize
|
|
1063
|
+
_tmp_normalize=$(mktemp)
|
|
1064
|
+
jq '{tasks: .}' "$pending_file" > "$_tmp_normalize" && mv "$_tmp_normalize" "$pending_file"
|
|
1065
|
+
rm -f "$_tmp_normalize"
|
|
1058
1066
|
fi
|
|
1059
1067
|
|
|
1060
1068
|
# Parse issues and add to pending queue
|
|
@@ -1468,6 +1476,8 @@ create_worktree() {
|
|
|
1468
1476
|
cargo build --quiet 2>/dev/null || true
|
|
1469
1477
|
fi
|
|
1470
1478
|
) &
|
|
1479
|
+
# Capture install PID for cleanup on exit
|
|
1480
|
+
WORKTREE_INSTALL_PIDS+=($!)
|
|
1471
1481
|
|
|
1472
1482
|
log_info "Created worktree: $worktree_path"
|
|
1473
1483
|
return 0
|
|
@@ -1776,6 +1786,14 @@ spawn_feature_stream() {
|
|
|
1776
1786
|
cleanup_parallel_streams() {
|
|
1777
1787
|
log_header "Cleaning Up Parallel Streams"
|
|
1778
1788
|
|
|
1789
|
+
# Kill background install processes
|
|
1790
|
+
for pid in "${WORKTREE_INSTALL_PIDS[@]}"; do
|
|
1791
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
1792
|
+
kill "$pid" 2>/dev/null || true
|
|
1793
|
+
fi
|
|
1794
|
+
done
|
|
1795
|
+
WORKTREE_INSTALL_PIDS=()
|
|
1796
|
+
|
|
1779
1797
|
# Kill all sessions
|
|
1780
1798
|
for stream in "${!WORKTREE_PIDS[@]}"; do
|
|
1781
1799
|
local pid="${WORKTREE_PIDS[$stream]}"
|
|
@@ -2294,11 +2312,12 @@ write_dashboard_state() {
|
|
|
2294
2312
|
local failed_tasks="[]"
|
|
2295
2313
|
local review_tasks="[]"
|
|
2296
2314
|
|
|
2297
|
-
|
|
2298
|
-
[ -f ".loki/queue/
|
|
2299
|
-
[ -f ".loki/queue/
|
|
2300
|
-
[ -f ".loki/queue/
|
|
2301
|
-
[ -f ".loki/queue/
|
|
2315
|
+
# Read queue files, normalizing {"tasks":[...]} format to plain array
|
|
2316
|
+
[ -f ".loki/queue/pending.json" ] && pending_tasks=$(jq 'if type == "object" then .tasks // [] else . end' ".loki/queue/pending.json" 2>/dev/null || echo "[]")
|
|
2317
|
+
[ -f ".loki/queue/in-progress.json" ] && in_progress_tasks=$(jq 'if type == "object" then .tasks // [] else . end' ".loki/queue/in-progress.json" 2>/dev/null || echo "[]")
|
|
2318
|
+
[ -f ".loki/queue/completed.json" ] && completed_tasks=$(jq 'if type == "object" then .tasks // [] else . end' ".loki/queue/completed.json" 2>/dev/null || echo "[]")
|
|
2319
|
+
[ -f ".loki/queue/failed.json" ] && failed_tasks=$(jq 'if type == "object" then .tasks // [] else . end' ".loki/queue/failed.json" 2>/dev/null || echo "[]")
|
|
2320
|
+
[ -f ".loki/queue/review.json" ] && review_tasks=$(jq 'if type == "object" then .tasks // [] else . end' ".loki/queue/review.json" 2>/dev/null || echo "[]")
|
|
2302
2321
|
|
|
2303
2322
|
# Get agents state
|
|
2304
2323
|
local agents="[]"
|
|
@@ -2351,11 +2370,12 @@ write_dashboard_state() {
|
|
|
2351
2370
|
council_state=$(cat ".loki/council/state.json" 2>/dev/null || echo '{"enabled":false}')
|
|
2352
2371
|
fi
|
|
2353
2372
|
|
|
2354
|
-
# Write comprehensive JSON state
|
|
2373
|
+
# Write comprehensive JSON state (atomic via temp file + mv)
|
|
2355
2374
|
local project_name=$(basename "$(pwd)")
|
|
2356
2375
|
local project_path=$(pwd)
|
|
2376
|
+
local _tmp_state="${output_file}.tmp"
|
|
2357
2377
|
|
|
2358
|
-
cat > "$
|
|
2378
|
+
cat > "$_tmp_state" << EOF
|
|
2359
2379
|
{
|
|
2360
2380
|
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
2361
2381
|
"version": "$version",
|
|
@@ -2396,6 +2416,7 @@ write_dashboard_state() {
|
|
|
2396
2416
|
"council": $council_state
|
|
2397
2417
|
}
|
|
2398
2418
|
EOF
|
|
2419
|
+
mv "$_tmp_state" "$output_file"
|
|
2399
2420
|
}
|
|
2400
2421
|
|
|
2401
2422
|
#===============================================================================
|
|
@@ -2419,13 +2440,15 @@ track_iteration_start() {
|
|
|
2419
2440
|
"provider=${PROVIDER_NAME:-claude}" \
|
|
2420
2441
|
"prd=${prd:-Codebase Analysis}"
|
|
2421
2442
|
|
|
2422
|
-
# Create task entry
|
|
2443
|
+
# Create task entry (escape PRD path for safe JSON embedding)
|
|
2444
|
+
local prd_escaped
|
|
2445
|
+
prd_escaped=$(printf '%s' "${prd:-Codebase Analysis}" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')
|
|
2423
2446
|
local task_json=$(cat <<EOF
|
|
2424
2447
|
{
|
|
2425
2448
|
"id": "$task_id",
|
|
2426
2449
|
"type": "iteration",
|
|
2427
2450
|
"title": "Iteration $iteration",
|
|
2428
|
-
"description": "PRD: ${
|
|
2451
|
+
"description": "PRD: ${prd_escaped}",
|
|
2429
2452
|
"status": "in_progress",
|
|
2430
2453
|
"priority": "medium",
|
|
2431
2454
|
"startedAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
@@ -4317,6 +4340,9 @@ build_prompt() {
|
|
|
4317
4340
|
local human_directive=""
|
|
4318
4341
|
if [ -n "${LOKI_HUMAN_INPUT:-}" ]; then
|
|
4319
4342
|
human_directive="HUMAN_DIRECTIVE (PRIORITY): $LOKI_HUMAN_INPUT Execute this directive BEFORE continuing normal tasks."
|
|
4343
|
+
# Clear after consumption so it doesn't repeat every iteration
|
|
4344
|
+
unset LOKI_HUMAN_INPUT
|
|
4345
|
+
rm -f "${TARGET_DIR:-.}/.loki/HUMAN_INPUT.md"
|
|
4320
4346
|
fi
|
|
4321
4347
|
|
|
4322
4348
|
# Queue task injection (from dashboard or API)
|
|
@@ -4883,8 +4909,8 @@ check_human_intervention() {
|
|
|
4883
4909
|
if [ -f "$loki_dir/PAUSE" ]; then
|
|
4884
4910
|
log_warn "PAUSE file detected - pausing execution"
|
|
4885
4911
|
notify_intervention_needed "Execution paused via PAUSE file"
|
|
4886
|
-
rm -f "$loki_dir/PAUSE"
|
|
4887
4912
|
handle_pause
|
|
4913
|
+
rm -f "$loki_dir/PAUSE"
|
|
4888
4914
|
return 1
|
|
4889
4915
|
fi
|
|
4890
4916
|
|
|
@@ -4933,6 +4959,12 @@ check_human_intervention() {
|
|
|
4933
4959
|
rm -f "$loki_dir/signals/COUNCIL_REVIEW_REQUESTED"
|
|
4934
4960
|
if type council_vote &>/dev/null && council_vote; then
|
|
4935
4961
|
log_header "COMPLETION COUNCIL: FORCE REVIEW - PROJECT COMPLETE"
|
|
4962
|
+
# Complete the missing steps: COMPLETED marker, memory consolidation, report
|
|
4963
|
+
touch "$loki_dir/COMPLETED"
|
|
4964
|
+
log_info "Running memory consolidation..."
|
|
4965
|
+
run_memory_consolidation
|
|
4966
|
+
notify_all_complete
|
|
4967
|
+
save_state ${RETRY_COUNT:-0} "council_force_approved" 0
|
|
4936
4968
|
return 2 # Stop
|
|
4937
4969
|
fi
|
|
4938
4970
|
log_info "Council force-review: voted to continue"
|
package/dashboard/__init__.py
CHANGED
package/dashboard/control.py
CHANGED
|
@@ -5,7 +5,7 @@ FastAPI-based session control endpoints for the Loki Mode dashboard.
|
|
|
5
5
|
Provides start/stop/pause/resume functionality and real-time status updates.
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
|
-
uvicorn dashboard.control:app --host 0.0.0.0 --port
|
|
8
|
+
uvicorn dashboard.control:app --host 0.0.0.0 --port 57374
|
|
9
9
|
# Or run with the CLI:
|
|
10
10
|
loki dashboard start
|
|
11
11
|
"""
|
|
@@ -510,5 +510,5 @@ async def asyncio_sleep(seconds: float):
|
|
|
510
510
|
# Run with uvicorn if executed directly
|
|
511
511
|
if __name__ == "__main__":
|
|
512
512
|
import uvicorn
|
|
513
|
-
port = int(os.environ.get("LOKI_DASHBOARD_PORT", "
|
|
513
|
+
port = int(os.environ.get("LOKI_DASHBOARD_PORT", "57374"))
|
|
514
514
|
uvicorn.run(app, host="0.0.0.0", port=port)
|