loki-mode 6.37.6 → 6.37.8
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 +74 -103
- package/autonomy/run.sh +6 -0
- package/completions/_loki +27 -11
- package/completions/loki.bash +9 -4
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +90 -6
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/web-app/dist/assets/{index-BVw_Fxig.js → index-Bwj6Ubaa.js} +1 -1
- package/web-app/dist/index.html +1 -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 minimal human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v6.37.
|
|
6
|
+
# Loki Mode v6.37.8
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
267
267
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
268
268
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
269
269
|
|
|
270
|
-
**v6.37.
|
|
270
|
+
**v6.37.8 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.37.
|
|
1
|
+
6.37.8
|
package/autonomy/loki
CHANGED
|
@@ -410,7 +410,7 @@ show_help() {
|
|
|
410
410
|
echo " api [cmd] Dashboard/API server (start|stop|status)"
|
|
411
411
|
echo " sandbox [cmd] Docker sandbox (start|stop|status|logs|shell|build)"
|
|
412
412
|
echo " notify [cmd] Send notifications (test|slack|discord|webhook|status)"
|
|
413
|
-
echo "
|
|
413
|
+
echo " telemetry [cmd] OpenTelemetry management (status|enable|disable|stop|start)"
|
|
414
414
|
echo " import Import GitHub issues as tasks"
|
|
415
415
|
echo " github [cmd] GitHub integration (sync|export|pr|status)"
|
|
416
416
|
echo " config [cmd] Manage configuration (show|init|edit|path|set|get)"
|
|
@@ -9744,7 +9744,8 @@ main() {
|
|
|
9744
9744
|
cmd_enterprise "$@"
|
|
9745
9745
|
;;
|
|
9746
9746
|
voice)
|
|
9747
|
-
|
|
9747
|
+
echo "Voice mode is planned for a future release. Track progress at github.com/asklokesh/loki-mode/issues/85"
|
|
9748
|
+
exit 0
|
|
9748
9749
|
;;
|
|
9749
9750
|
secrets)
|
|
9750
9751
|
cmd_secrets "$@"
|
|
@@ -13176,95 +13177,9 @@ for check, passed in checks.items():
|
|
|
13176
13177
|
|
|
13177
13178
|
# Voice input commands
|
|
13178
13179
|
cmd_voice() {
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
# Check fallback locations for voice script
|
|
13183
|
-
if [ ! -f "$VOICE_SCRIPT" ]; then
|
|
13184
|
-
# Try relative to loki CLI location
|
|
13185
|
-
local loki_dir
|
|
13186
|
-
loki_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13187
|
-
VOICE_SCRIPT="$loki_dir/voice.sh"
|
|
13188
|
-
fi
|
|
13189
|
-
|
|
13190
|
-
if [ ! -f "$VOICE_SCRIPT" ]; then
|
|
13191
|
-
echo -e "${RED}Error: Voice module not found${NC}"
|
|
13192
|
-
echo "Expected at: $SKILL_DIR/autonomy/voice.sh"
|
|
13193
|
-
echo ""
|
|
13194
|
-
echo "Voice input requires the voice.sh module."
|
|
13195
|
-
echo "This feature may not be available in all installations."
|
|
13196
|
-
exit 1
|
|
13197
|
-
fi
|
|
13198
|
-
|
|
13199
|
-
case "$subcommand" in
|
|
13200
|
-
status)
|
|
13201
|
-
"$VOICE_SCRIPT" status
|
|
13202
|
-
;;
|
|
13203
|
-
listen)
|
|
13204
|
-
echo -e "${BOLD}Starting voice input...${NC}"
|
|
13205
|
-
local text
|
|
13206
|
-
text=$("$VOICE_SCRIPT" listen)
|
|
13207
|
-
if [ -n "$text" ]; then
|
|
13208
|
-
echo ""
|
|
13209
|
-
echo -e "${GREEN}Transcribed text:${NC}"
|
|
13210
|
-
echo "$text"
|
|
13211
|
-
fi
|
|
13212
|
-
;;
|
|
13213
|
-
dictate)
|
|
13214
|
-
local output="${2:-prd-voice.md}"
|
|
13215
|
-
echo -e "${BOLD}Starting guided PRD dictation...${NC}"
|
|
13216
|
-
echo ""
|
|
13217
|
-
"$VOICE_SCRIPT" dictate "$output"
|
|
13218
|
-
if [ -f "$output" ]; then
|
|
13219
|
-
echo ""
|
|
13220
|
-
echo -e "${GREEN}PRD created: $output${NC}"
|
|
13221
|
-
echo ""
|
|
13222
|
-
echo "Start Loki Mode with:"
|
|
13223
|
-
echo " loki start $output"
|
|
13224
|
-
fi
|
|
13225
|
-
;;
|
|
13226
|
-
speak)
|
|
13227
|
-
shift
|
|
13228
|
-
if [ $# -eq 0 ]; then
|
|
13229
|
-
echo -e "${RED}Usage: loki voice speak MESSAGE${NC}"
|
|
13230
|
-
exit 1
|
|
13231
|
-
fi
|
|
13232
|
-
"$VOICE_SCRIPT" speak "$*"
|
|
13233
|
-
;;
|
|
13234
|
-
start)
|
|
13235
|
-
# Dictate PRD and start Loki Mode
|
|
13236
|
-
local prd_file="${2:-prd-voice-$(date +%Y%m%d%H%M%S).md}"
|
|
13237
|
-
echo -e "${BOLD}Voice-activated PRD creation...${NC}"
|
|
13238
|
-
"$VOICE_SCRIPT" dictate "$prd_file"
|
|
13239
|
-
if [ -f "$prd_file" ]; then
|
|
13240
|
-
echo ""
|
|
13241
|
-
echo -e "${GREEN}PRD created. Starting Loki Mode...${NC}"
|
|
13242
|
-
cmd_start "$prd_file"
|
|
13243
|
-
fi
|
|
13244
|
-
;;
|
|
13245
|
-
--help|-h|help)
|
|
13246
|
-
echo -e "${BOLD}loki voice${NC} - Voice input for PRD creation"
|
|
13247
|
-
echo ""
|
|
13248
|
-
echo "Usage: loki voice <command> [options]"
|
|
13249
|
-
echo ""
|
|
13250
|
-
echo "Commands:"
|
|
13251
|
-
echo " status Check voice input capabilities"
|
|
13252
|
-
echo " listen Listen and transcribe voice input"
|
|
13253
|
-
echo " dictate [FILE] Guided PRD dictation (default: prd-voice.md)"
|
|
13254
|
-
echo " speak MESSAGE Text-to-speech output"
|
|
13255
|
-
echo " start [FILE] Dictate PRD and start Loki Mode immediately"
|
|
13256
|
-
echo ""
|
|
13257
|
-
echo "Requirements:"
|
|
13258
|
-
echo " macOS: Enable Dictation in System Settings > Keyboard"
|
|
13259
|
-
echo " Or: Set OPENAI_API_KEY for Whisper API transcription"
|
|
13260
|
-
echo " Or: pip install openai-whisper for local transcription"
|
|
13261
|
-
;;
|
|
13262
|
-
*)
|
|
13263
|
-
echo -e "${RED}Unknown voice command: $subcommand${NC}"
|
|
13264
|
-
echo "Run 'loki voice help' for usage."
|
|
13265
|
-
exit 1
|
|
13266
|
-
;;
|
|
13267
|
-
esac
|
|
13180
|
+
# Voice mode is planned for a future release (#85)
|
|
13181
|
+
echo "Voice mode is planned for a future release. Track progress at github.com/asklokesh/loki-mode/issues/85"
|
|
13182
|
+
return 0
|
|
13268
13183
|
}
|
|
13269
13184
|
|
|
13270
13185
|
# Enterprise features (optional - requires env vars)
|
|
@@ -13752,9 +13667,22 @@ cmd_telemetry() {
|
|
|
13752
13667
|
echo -e "${BOLD}Telemetry Status${NC}"
|
|
13753
13668
|
echo ""
|
|
13754
13669
|
|
|
13670
|
+
# Check persistent opt-out (~/.loki/config)
|
|
13671
|
+
local global_config="${HOME}/.loki/config"
|
|
13672
|
+
local persistently_disabled=false
|
|
13673
|
+
if [ -f "$global_config" ] && grep -q "^TELEMETRY_DISABLED=true" "$global_config" 2>/dev/null; then
|
|
13674
|
+
persistently_disabled=true
|
|
13675
|
+
fi
|
|
13676
|
+
|
|
13677
|
+
if [ "$persistently_disabled" = true ]; then
|
|
13678
|
+
echo -e " Opt-out: ${RED}disabled persistently${NC} (loki telemetry start to re-enable)"
|
|
13679
|
+
fi
|
|
13680
|
+
|
|
13755
13681
|
local endpoint="${LOKI_OTEL_ENDPOINT:-}"
|
|
13756
|
-
if [ -n "$endpoint" ]; then
|
|
13682
|
+
if [ -n "$endpoint" ] && [ "$persistently_disabled" = false ]; then
|
|
13757
13683
|
echo -e " Endpoint: ${GREEN}$endpoint${NC}"
|
|
13684
|
+
elif [ "$persistently_disabled" = true ]; then
|
|
13685
|
+
echo -e " Endpoint: ${YELLOW}ignored (opted out)${NC}"
|
|
13758
13686
|
else
|
|
13759
13687
|
echo -e " Endpoint: ${YELLOW}not configured${NC}"
|
|
13760
13688
|
fi
|
|
@@ -13787,6 +13715,15 @@ try {
|
|
|
13787
13715
|
|
|
13788
13716
|
enable)
|
|
13789
13717
|
local endpoint="${1:-http://localhost:4318}"
|
|
13718
|
+
|
|
13719
|
+
# Check persistent opt-out
|
|
13720
|
+
local global_config="${HOME}/.loki/config"
|
|
13721
|
+
if [ -f "$global_config" ] && grep -q "^TELEMETRY_DISABLED=true" "$global_config" 2>/dev/null; then
|
|
13722
|
+
echo -e "${YELLOW}Telemetry is persistently disabled.${NC}"
|
|
13723
|
+
echo "Run 'loki telemetry start' first to re-enable."
|
|
13724
|
+
return 1
|
|
13725
|
+
fi
|
|
13726
|
+
|
|
13790
13727
|
echo -e "${BOLD}Enabling telemetry${NC}"
|
|
13791
13728
|
|
|
13792
13729
|
# Save to config
|
|
@@ -13830,15 +13767,49 @@ with open('$config_file', 'w') as f:
|
|
|
13830
13767
|
echo " Unset LOKI_OTEL_ENDPOINT in your shell for immediate effect."
|
|
13831
13768
|
;;
|
|
13832
13769
|
|
|
13770
|
+
stop)
|
|
13771
|
+
# Persistent opt-out across all sessions
|
|
13772
|
+
local global_config="${HOME}/.loki/config"
|
|
13773
|
+
mkdir -p "${HOME}/.loki"
|
|
13774
|
+
|
|
13775
|
+
# Remove existing TELEMETRY_DISABLED line if present, then add
|
|
13776
|
+
if [ -f "$global_config" ]; then
|
|
13777
|
+
grep -v "^TELEMETRY_DISABLED=" "$global_config" > "${global_config}.tmp" 2>/dev/null || true
|
|
13778
|
+
mv "${global_config}.tmp" "$global_config"
|
|
13779
|
+
fi
|
|
13780
|
+
echo "TELEMETRY_DISABLED=true" >> "$global_config"
|
|
13781
|
+
|
|
13782
|
+
echo -e "${BOLD}Telemetry permanently disabled${NC}"
|
|
13783
|
+
echo ""
|
|
13784
|
+
echo " Opt-out saved to: $global_config"
|
|
13785
|
+
echo " This persists across all sessions and new runs."
|
|
13786
|
+
echo " Run 'loki telemetry start' to re-enable."
|
|
13787
|
+
;;
|
|
13788
|
+
|
|
13789
|
+
start)
|
|
13790
|
+
# Remove persistent opt-out
|
|
13791
|
+
local global_config="${HOME}/.loki/config"
|
|
13792
|
+
if [ -f "$global_config" ]; then
|
|
13793
|
+
grep -v "^TELEMETRY_DISABLED=" "$global_config" > "${global_config}.tmp" 2>/dev/null || true
|
|
13794
|
+
mv "${global_config}.tmp" "$global_config"
|
|
13795
|
+
fi
|
|
13796
|
+
|
|
13797
|
+
echo -e "${BOLD}Telemetry opt-out removed${NC}"
|
|
13798
|
+
echo ""
|
|
13799
|
+
echo " Telemetry can now be enabled with 'loki telemetry enable [endpoint]'."
|
|
13800
|
+
;;
|
|
13801
|
+
|
|
13833
13802
|
--help|-h|help)
|
|
13834
|
-
echo -e "${BOLD}loki telemetry${NC} - OpenTelemetry management
|
|
13803
|
+
echo -e "${BOLD}loki telemetry${NC} - OpenTelemetry management"
|
|
13835
13804
|
echo ""
|
|
13836
13805
|
echo "Usage: loki telemetry <command>"
|
|
13837
13806
|
echo ""
|
|
13838
13807
|
echo "Commands:"
|
|
13839
13808
|
echo " status Show current telemetry config"
|
|
13840
13809
|
echo " enable [endpoint] Enable OTEL (default: http://localhost:4318)"
|
|
13841
|
-
echo " disable Disable OTEL"
|
|
13810
|
+
echo " disable Disable OTEL for current project"
|
|
13811
|
+
echo " stop Permanently opt out of telemetry across all sessions"
|
|
13812
|
+
echo " start Remove persistent opt-out, re-enable telemetry"
|
|
13842
13813
|
echo ""
|
|
13843
13814
|
;;
|
|
13844
13815
|
*)
|
|
@@ -14648,19 +14619,19 @@ cmd_agent() {
|
|
|
14648
14619
|
local filter_swarm="${1:-}"
|
|
14649
14620
|
echo -e "${BOLD}Agent Types${NC}"
|
|
14650
14621
|
echo ""
|
|
14651
|
-
python3 << PYEOF
|
|
14652
|
-
import json, sys
|
|
14622
|
+
TYPES_FILE="$types_file" FILTER_SWARM="$filter_swarm" FILTER_SWARM2="${2:-}" python3 << 'PYEOF'
|
|
14623
|
+
import json, sys, os
|
|
14653
14624
|
|
|
14654
|
-
with open("
|
|
14625
|
+
with open(os.environ["TYPES_FILE"]) as f:
|
|
14655
14626
|
agents = json.load(f)
|
|
14656
14627
|
|
|
14657
|
-
filter_swarm = "
|
|
14628
|
+
filter_swarm = os.environ.get("FILTER_SWARM", "")
|
|
14658
14629
|
if filter_swarm and filter_swarm.startswith("--swarm"):
|
|
14659
14630
|
# Handle --swarm=X or --swarm X
|
|
14660
14631
|
if "=" in filter_swarm:
|
|
14661
14632
|
filter_swarm = filter_swarm.split("=")[1]
|
|
14662
14633
|
else:
|
|
14663
|
-
filter_swarm = "
|
|
14634
|
+
filter_swarm = os.environ.get("FILTER_SWARM2", "")
|
|
14664
14635
|
|
|
14665
14636
|
swarms = {}
|
|
14666
14637
|
for a in agents:
|
|
@@ -14690,13 +14661,13 @@ PYEOF
|
|
|
14690
14661
|
echo -e "${RED}Usage: loki agent info <type>${NC}"
|
|
14691
14662
|
return 1
|
|
14692
14663
|
fi
|
|
14693
|
-
python3 << PYEOF
|
|
14694
|
-
import json, sys
|
|
14664
|
+
TYPES_FILE="$types_file" AGENT_TYPE="$agent_type" python3 << 'PYEOF'
|
|
14665
|
+
import json, sys, os
|
|
14695
14666
|
|
|
14696
|
-
with open("
|
|
14667
|
+
with open(os.environ["TYPES_FILE"]) as f:
|
|
14697
14668
|
agents = json.load(f)
|
|
14698
14669
|
|
|
14699
|
-
target = "
|
|
14670
|
+
target = os.environ["AGENT_TYPE"]
|
|
14700
14671
|
found = None
|
|
14701
14672
|
for a in agents:
|
|
14702
14673
|
if a["type"] == target:
|
package/autonomy/run.sh
CHANGED
|
@@ -602,6 +602,12 @@ if [ -f "${SCRIPT_DIR}/playwright-verify.sh" ]; then
|
|
|
602
602
|
fi
|
|
603
603
|
|
|
604
604
|
# Anonymous usage telemetry (opt-out: LOKI_TELEMETRY_DISABLED=true or DO_NOT_TRACK=1)
|
|
605
|
+
# Also check persistent opt-out from ~/.loki/config (#77)
|
|
606
|
+
if [ -f "${HOME}/.loki/config" ] && grep -q "^TELEMETRY_DISABLED=true" "${HOME}/.loki/config" 2>/dev/null; then
|
|
607
|
+
LOKI_TELEMETRY_DISABLED=true
|
|
608
|
+
export LOKI_TELEMETRY_DISABLED
|
|
609
|
+
unset LOKI_OTEL_ENDPOINT
|
|
610
|
+
fi
|
|
605
611
|
TELEMETRY_SCRIPT="$SCRIPT_DIR/telemetry.sh"
|
|
606
612
|
if [ -f "$TELEMETRY_SCRIPT" ]; then
|
|
607
613
|
# shellcheck source=telemetry.sh
|
package/completions/_loki
CHANGED
|
@@ -44,8 +44,11 @@ function _loki {
|
|
|
44
44
|
projects)
|
|
45
45
|
_loki_projects
|
|
46
46
|
;;
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
telemetry)
|
|
48
|
+
_loki_telemetry
|
|
49
|
+
;;
|
|
50
|
+
agent)
|
|
51
|
+
_loki_agent
|
|
49
52
|
;;
|
|
50
53
|
status)
|
|
51
54
|
_arguments \
|
|
@@ -121,7 +124,8 @@ function _loki_commands {
|
|
|
121
124
|
'dogfood:Self-development statistics'
|
|
122
125
|
'projects:Project registry commands'
|
|
123
126
|
'enterprise:Enterprise feature commands'
|
|
124
|
-
'
|
|
127
|
+
'telemetry:OpenTelemetry management'
|
|
128
|
+
'agent:Agent type dispatch'
|
|
125
129
|
'doctor:Check system prerequisites'
|
|
126
130
|
'onboard:Analyze repo and generate CLAUDE.md'
|
|
127
131
|
'metrics:Session productivity report'
|
|
@@ -294,17 +298,29 @@ function _loki_projects {
|
|
|
294
298
|
_describe -t cmds 'projects subcommand' cmds
|
|
295
299
|
}
|
|
296
300
|
|
|
297
|
-
function
|
|
301
|
+
function _loki_telemetry {
|
|
302
|
+
local -a cmds
|
|
303
|
+
cmds=(
|
|
304
|
+
'status:Show telemetry config'
|
|
305
|
+
'enable:Enable OTEL'
|
|
306
|
+
'disable:Disable OTEL for current project'
|
|
307
|
+
'stop:Permanently opt out across all sessions'
|
|
308
|
+
'start:Remove persistent opt-out'
|
|
309
|
+
'help:Telemetry help'
|
|
310
|
+
)
|
|
311
|
+
_describe -t cmds 'telemetry subcommand' cmds
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function _loki_agent {
|
|
298
315
|
local -a cmds
|
|
299
316
|
cmds=(
|
|
300
|
-
'
|
|
301
|
-
'
|
|
302
|
-
'
|
|
303
|
-
'
|
|
304
|
-
'
|
|
305
|
-
'help:Voice help'
|
|
317
|
+
'list:List agent types'
|
|
318
|
+
'info:Show agent type details'
|
|
319
|
+
'run:Run agent with prompt'
|
|
320
|
+
'start:Start agent in background'
|
|
321
|
+
'help:Agent help'
|
|
306
322
|
)
|
|
307
|
-
_describe -t cmds '
|
|
323
|
+
_describe -t cmds 'agent subcommand' cmds
|
|
308
324
|
}
|
|
309
325
|
|
|
310
326
|
function _loki_reset {
|
package/completions/loki.bash
CHANGED
|
@@ -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
|
|
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 version completions help"
|
|
9
9
|
|
|
10
10
|
# 1. If we are on the first argument (subcommand)
|
|
11
11
|
if [[ $cword -eq 1 ]]; then
|
|
@@ -83,9 +83,14 @@ _loki_completion() {
|
|
|
83
83
|
COMPREPLY=( $(compgen -W "${projects_cmds}" -- "$cur") )
|
|
84
84
|
;;
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
local
|
|
88
|
-
COMPREPLY=( $(compgen -W "${
|
|
86
|
+
telemetry)
|
|
87
|
+
local telemetry_cmds="status enable disable stop start help"
|
|
88
|
+
COMPREPLY=( $(compgen -W "${telemetry_cmds}" -- "$cur") )
|
|
89
|
+
;;
|
|
90
|
+
|
|
91
|
+
agent)
|
|
92
|
+
local agent_cmds="list info run start help"
|
|
93
|
+
COMPREPLY=( $(compgen -W "${agent_cmds}" -- "$cur") )
|
|
89
94
|
;;
|
|
90
95
|
|
|
91
96
|
syslog)
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -162,6 +162,7 @@ class ProjectCreate(BaseModel):
|
|
|
162
162
|
name: str = Field(..., min_length=1, max_length=255)
|
|
163
163
|
description: Optional[str] = None
|
|
164
164
|
prd_path: Optional[str] = None
|
|
165
|
+
tenant_id: int = Field(..., gt=0, description="Tenant ID (required, must be positive)")
|
|
165
166
|
|
|
166
167
|
@field_validator("name")
|
|
167
168
|
@classmethod
|
|
@@ -778,6 +779,7 @@ async def create_project(
|
|
|
778
779
|
name=project.name,
|
|
779
780
|
description=project.description,
|
|
780
781
|
prd_path=project.prd_path,
|
|
782
|
+
tenant_id=project.tenant_id,
|
|
781
783
|
)
|
|
782
784
|
db.add(db_project)
|
|
783
785
|
await db.flush()
|
|
@@ -2662,10 +2664,43 @@ async def pause_session():
|
|
|
2662
2664
|
"""Pause the current session by creating PAUSE file."""
|
|
2663
2665
|
if not _control_limiter.check("control"):
|
|
2664
2666
|
raise HTTPException(status_code=429, detail="Rate limit exceeded")
|
|
2665
|
-
|
|
2667
|
+
|
|
2668
|
+
loki_dir = _get_loki_dir()
|
|
2669
|
+
pid_file = loki_dir / "loki.pid"
|
|
2670
|
+
|
|
2671
|
+
# Verify loki process is running before attempting pause
|
|
2672
|
+
process_running = False
|
|
2673
|
+
if pid_file.exists():
|
|
2674
|
+
try:
|
|
2675
|
+
pid = int(pid_file.read_text().strip())
|
|
2676
|
+
os.kill(pid, 0) # Signal 0: check existence without killing
|
|
2677
|
+
process_running = True
|
|
2678
|
+
except (ValueError, OSError, ProcessLookupError):
|
|
2679
|
+
pass
|
|
2680
|
+
|
|
2681
|
+
pause_file = loki_dir / "PAUSE"
|
|
2666
2682
|
pause_file.parent.mkdir(parents=True, exist_ok=True)
|
|
2667
2683
|
pause_file.write_text(datetime.now(timezone.utc).isoformat())
|
|
2668
|
-
|
|
2684
|
+
|
|
2685
|
+
if not process_running:
|
|
2686
|
+
return JSONResponse(
|
|
2687
|
+
status_code=503,
|
|
2688
|
+
content={"success": False, "message": "Session process is not running; pause signal may have no effect"},
|
|
2689
|
+
)
|
|
2690
|
+
|
|
2691
|
+
# Poll up to 5s to confirm process is still alive with PAUSE file present
|
|
2692
|
+
for _ in range(10):
|
|
2693
|
+
try:
|
|
2694
|
+
os.kill(pid, 0)
|
|
2695
|
+
return {"success": True, "message": "Session paused", "process_verified": True}
|
|
2696
|
+
except OSError:
|
|
2697
|
+
break
|
|
2698
|
+
await asyncio.sleep(0.5)
|
|
2699
|
+
|
|
2700
|
+
return JSONResponse(
|
|
2701
|
+
status_code=503,
|
|
2702
|
+
content={"success": False, "message": "Session process exited unexpectedly after pause signal"},
|
|
2703
|
+
)
|
|
2669
2704
|
|
|
2670
2705
|
|
|
2671
2706
|
@app.post("/api/control/resume", dependencies=[Depends(auth.require_scope("control"))])
|
|
@@ -2673,13 +2708,58 @@ async def resume_session():
|
|
|
2673
2708
|
"""Resume a paused session by removing PAUSE/STOP files."""
|
|
2674
2709
|
if not _control_limiter.check("control"):
|
|
2675
2710
|
raise HTTPException(status_code=429, detail="Rate limit exceeded")
|
|
2711
|
+
|
|
2712
|
+
loki_dir = _get_loki_dir()
|
|
2713
|
+
pid_file = loki_dir / "loki.pid"
|
|
2714
|
+
|
|
2715
|
+
# Verify loki process is running before attempting resume
|
|
2716
|
+
process_running = False
|
|
2717
|
+
pid = 0
|
|
2718
|
+
if pid_file.exists():
|
|
2719
|
+
try:
|
|
2720
|
+
pid = int(pid_file.read_text().strip())
|
|
2721
|
+
os.kill(pid, 0) # Signal 0: check existence without killing
|
|
2722
|
+
process_running = True
|
|
2723
|
+
except (ValueError, OSError, ProcessLookupError):
|
|
2724
|
+
pass
|
|
2725
|
+
|
|
2726
|
+
if not process_running:
|
|
2727
|
+
# Still remove the files for cleanup, but return 503
|
|
2728
|
+
for fname in ["PAUSE", "STOP"]:
|
|
2729
|
+
fpath = loki_dir / fname
|
|
2730
|
+
try:
|
|
2731
|
+
fpath.unlink(missing_ok=True)
|
|
2732
|
+
except Exception:
|
|
2733
|
+
pass
|
|
2734
|
+
return JSONResponse(
|
|
2735
|
+
status_code=503,
|
|
2736
|
+
content={"success": False, "message": "Session did not respond to resume signal"},
|
|
2737
|
+
)
|
|
2738
|
+
|
|
2676
2739
|
for fname in ["PAUSE", "STOP"]:
|
|
2677
|
-
fpath =
|
|
2740
|
+
fpath = loki_dir / fname
|
|
2678
2741
|
try:
|
|
2679
2742
|
fpath.unlink(missing_ok=True)
|
|
2680
2743
|
except Exception:
|
|
2681
2744
|
pass
|
|
2682
|
-
|
|
2745
|
+
|
|
2746
|
+
# Poll up to 5s to verify the process is still running and acknowledged the resume
|
|
2747
|
+
for _ in range(10):
|
|
2748
|
+
try:
|
|
2749
|
+
os.kill(pid, 0)
|
|
2750
|
+
if not (loki_dir / "PAUSE").exists():
|
|
2751
|
+
return {"success": True, "message": "Session resumed", "process_verified": True}
|
|
2752
|
+
except OSError:
|
|
2753
|
+
return JSONResponse(
|
|
2754
|
+
status_code=503,
|
|
2755
|
+
content={"success": False, "message": "Session did not respond to resume signal"},
|
|
2756
|
+
)
|
|
2757
|
+
await asyncio.sleep(0.5)
|
|
2758
|
+
|
|
2759
|
+
return JSONResponse(
|
|
2760
|
+
status_code=503,
|
|
2761
|
+
content={"success": False, "message": "Session did not respond to resume signal"},
|
|
2762
|
+
)
|
|
2683
2763
|
|
|
2684
2764
|
|
|
2685
2765
|
@app.post("/api/control/stop", dependencies=[Depends(auth.require_scope("control"))])
|
|
@@ -2774,7 +2854,7 @@ def _calculate_model_cost(model: str, input_tokens: int, output_tokens: int) ->
|
|
|
2774
2854
|
pricing = pricing_table.get(model.lower(), pricing_table.get("sonnet", {}))
|
|
2775
2855
|
input_cost = (input_tokens / 1_000_000) * pricing.get("input", 3.00)
|
|
2776
2856
|
output_cost = (output_tokens / 1_000_000) * pricing.get("output", 15.00)
|
|
2777
|
-
return input_cost + output_cost
|
|
2857
|
+
return round(input_cost + output_cost, 4)
|
|
2778
2858
|
|
|
2779
2859
|
|
|
2780
2860
|
@app.get("/api/cost")
|
|
@@ -2868,7 +2948,11 @@ async def get_cost():
|
|
|
2868
2948
|
"total_input_tokens": total_input,
|
|
2869
2949
|
"total_output_tokens": total_output,
|
|
2870
2950
|
"estimated_cost_usd": round(estimated_cost, 4),
|
|
2871
|
-
"by_phase":
|
|
2951
|
+
"by_phase": {k: {
|
|
2952
|
+
"input_tokens": v["input_tokens"],
|
|
2953
|
+
"output_tokens": v["output_tokens"],
|
|
2954
|
+
"cost_usd": round(v["cost_usd"], 4),
|
|
2955
|
+
} for k, v in by_phase.items()},
|
|
2872
2956
|
"by_model": {k: {
|
|
2873
2957
|
"input_tokens": v["input_tokens"],
|
|
2874
2958
|
"output_tokens": v["output_tokens"],
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -62,5 +62,5 @@ Describe your project here...
|
|
|
62
62
|
|
|
63
63
|
- Requirement 1
|
|
64
64
|
- Requirement 2
|
|
65
|
-
`)}},[]),Pl=async()=>{if(!(!M.trim()||tt)){Ql(!0),wl(null),ft(!0);try{const _=await ql.planSession(M,ll);wl(_)}catch{wl({complexity:"unknown",cost_estimate:"N/A",iterations:0,phases:[],output_text:"Failed to run loki plan. The CLI may not be available.",returncode:1})}finally{Ql(!1)}}},S=async()=>{if(!(!M.trim()||g||jl)){ft(!1),Ol(!0);try{await f(M,ll,O.trim()||void 0,Al?"quick":void 0)}finally{Ol(!1)}}};return i.jsxs(i.Fragment,{children:[Xl&&i.jsx(z0,{plan:Yl,loading:tt,onConfirm:S,onCancel:()=>ft(!1)}),i.jsxs("div",{className:"glass p-6 flex flex-col",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Product Requirements"}),i.jsx("div",{className:"flex items-center gap-2",children:i.jsxs("div",{className:"relative",children:[i.jsx("button",{onClick:()=>F(!al),className:"text-xs font-medium px-3 py-1.5 rounded-xl border border-primary/20 text-primary hover:bg-primary/5 transition-colors",children:L||"Templates"}),al&&i.jsx("div",{className:"absolute right-0 top-full mt-1 w-56 glass-subtle rounded-xl overflow-hidden z-20 shadow-glass",children:i.jsxs("div",{className:"py-1 max-h-64 overflow-y-auto terminal-scroll",children:[gl&&i.jsx("div",{className:"px-3 py-2 text-xs text-warning border-b border-warning/10",children:"Could not load templates from server. Check that the backend is running."}),!gl&&Nl.length===0&&i.jsx("div",{className:"px-3 py-2 text-xs text-slate",children:"Loading..."}),Nl.map(_=>i.jsx("button",{onClick:()=>Vl(_.filename,_.name),className:"w-full text-left px-3 py-2 text-sm text-charcoal hover:bg-primary/5 transition-colors",children:_.name},_.filename))]})})]})})]}),i.jsx("textarea",{value:M,onChange:_=>R(_.target.value),placeholder:"Paste your PRD here, or select a template above to get started...",className:"flex-1 min-h-[280px] w-full bg-white/40 rounded-xl border border-white/30 px-4 py-3 text-sm font-mono text-charcoal placeholder:text-primary-wash resize-none focus:outline-none focus:ring-2 focus:ring-accent-product/20 focus:border-accent-product/30 transition-all",spellCheck:!1}),i.jsxs("div",{className:"mt-3",children:[i.jsx("label",{className:"block text-xs text-slate font-medium mb-1 uppercase tracking-wider",children:"Project Directory"}),i.jsx("input",{type:"text",value:O,onChange:_=>Q(_.target.value),placeholder:"Leave blank to auto-create, or type a path (e.g. /Users/you/my-project)",className:"w-full bg-white/40 rounded-xl border border-white/30 px-4 py-2 text-sm font-mono text-charcoal placeholder:text-primary-wash/70 focus:outline-none focus:ring-2 focus:ring-accent-product/20 focus:border-accent-product/30 transition-all",spellCheck:!1}),i.jsx("p",{className:"text-[10px] text-slate mt-1",children:"Type a path or leave blank to auto-create under ~/purple-lab-projects/"})]}),p&&i.jsx("div",{className:"mt-3 px-3 py-2 rounded-lg bg-danger/10 border border-danger/20 text-danger text-xs font-medium",children:p}),i.jsxs("div",{className:"flex items-center gap-3 mt-4",children:[i.jsxs("button",{onClick:()=>k(!Al),title:"Quick Mode: 3 iterations max, faster builds",className:`flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-semibold border transition-all ${Al?"bg-accent-product/10 border-accent-product/30 text-accent-product":"border-white/30 text-slate hover:text-charcoal hover:bg-white/20"}`,children:[i.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${Al?"bg-accent-product":"bg-slate/40"}`}),"Quick"]}),i.jsx("div",{className:"flex-1"}),i.jsxs("span",{className:"text-xs text-slate font-mono",children:[M.length.toLocaleString()," chars"]}),i.jsx("button",{onClick:Pl,disabled:!M.trim()||g||tt,className:`px-4 py-2.5 rounded-xl text-sm font-semibold border transition-all ${!M.trim()||g||tt?"border-white/20 text-slate/40 cursor-not-allowed":"border-accent-product/30 text-accent-product hover:bg-accent-product/5"}`,children:tt?"Analyzing...":"Estimate"}),i.jsx("button",{onClick:S,disabled:!M.trim()||g||jl,className:`px-6 py-2.5 rounded-xl text-sm font-semibold transition-all ${!M.trim()||g||jl?"bg-surface/50 text-slate cursor-not-allowed":"bg-accent-product text-white hover:bg-accent-product/90 shadow-glass-subtle"}`,children:jl?"Starting...":g?"Building...":"Start Build"})]})]})]})}const Cn=[{key:"reason",label:"Reason",description:"Analyzing task, planning approach"},{key:"act",label:"Act",description:"Implementing changes, writing code"},{key:"reflect",label:"Reflect",description:"Reviewing output, self-critique"},{key:"verify",label:"Verify",description:"Testing, validation, quality gates"}];function _0(f){const g=f.toLowerCase();return g.includes("reason")||g.includes("plan")?"reason":g.includes("act")||g.includes("implement")||g.includes("code")?"act":g.includes("reflect")||g.includes("review")?"reflect":g.includes("verify")||g.includes("test")||g.includes("check")?"verify":"idle"}function M0({currentPhase:f,iteration:g}){const p=_0(f);return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"RARV Cycle"}),i.jsxs("span",{className:"font-mono text-xs text-slate",children:["Iteration ",g]})]}),i.jsx("div",{className:"flex items-center justify-center mb-5",children:i.jsxs("svg",{viewBox:"0 0 120 120",className:"w-28 h-28",children:[Cn.map((o,T)=>{const M=o.key===p,R=p!=="idle"&&Cn.findIndex(q=>q.key===p)>T,L=(T*90-90)*(Math.PI/180),D=60+40*Math.cos(L),N=60+40*Math.sin(L);return i.jsxs("g",{children:[T<Cn.length-1&&i.jsx("line",{x1:D,y1:N,x2:60+40*Math.cos(((T+1)*90-90)*(Math.PI/180)),y2:60+40*Math.sin(((T+1)*90-90)*(Math.PI/180)),stroke:R?"#3D52A0":"#ADBBDA",strokeWidth:R?2:1,strokeDasharray:R?"none":"4 3"}),T===Cn.length-1&&i.jsx("line",{x1:D,y1:N,x2:60+40*Math.cos(-90*(Math.PI/180)),y2:60+40*Math.sin(-90*(Math.PI/180)),stroke:"#ADBBDA",strokeWidth:1,strokeDasharray:"4 3"}),i.jsx("circle",{cx:D,cy:N,r:M?14:10,fill:M?"#3D52A0":R?"#7091E6":"#EDE8F5",stroke:M?"#6C63FF":R?"#3D52A0":"#ADBBDA",strokeWidth:M?3:1.5,className:M?"phase-active":""}),i.jsx("text",{x:D,y:N+(T===0?-20:T===2?24:0),textAnchor:"middle",className:"text-[9px] font-semibold fill-charcoal",dx:T===1?22:T===3?-22:0,children:o.label[0]})]},o.key)}),i.jsx("text",{x:"60",y:"64",textAnchor:"middle",className:"text-lg font-bold font-mono fill-primary",children:g})]})}),i.jsx("div",{className:"space-y-2",children:Cn.map(o=>{const T=o.key===p;return i.jsxs("div",{className:`flex items-center gap-3 px-3 py-2 rounded-xl transition-all duration-200 ${T?"bg-primary/8 border border-primary/20":"opacity-50"}`,children:[i.jsx("div",{className:`w-2.5 h-2.5 rounded-full flex-shrink-0 ${T?"bg-primary phase-active":"bg-surface"}`}),i.jsxs("div",{children:[i.jsx("span",{className:`text-sm font-semibold ${T?"text-primary":"text-slate"}`,children:o.label}),T&&i.jsx("p",{className:"text-xs text-slate mt-0.5",children:o.description})]})]},o.key)})})]})}const Od={architect:"bg-accent-product/10 text-accent-product border-accent-product/20",developer:"bg-primary/10 text-primary border-primary/20",tester:"bg-success/10 text-success border-success/20",reviewer:"bg-warning/10 text-warning border-warning/20",planner:"bg-primary-wash/20 text-charcoal border-primary-wash/30",default:"bg-surface/30 text-slate border-surface/50"};function O0(f){const g=f.toLowerCase();for(const[p,o]of Object.entries(Od))if(g.includes(p))return o;return Od.default}function D0({agents:f,loading:g}){const p=(f==null?void 0:f.filter(T=>T.alive))||[],o=(f==null?void 0:f.filter(T=>!T.alive))||[];return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Agents"}),i.jsxs("span",{className:"font-mono text-xs text-slate",children:[p.length," active"]})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading agents..."}),!g&&(f==null?void 0:f.length)===0&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No agents running"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Start a build to spawn agents"})]}),p.length>0&&i.jsx("div",{className:"space-y-2 mb-4",children:p.map(T=>i.jsx(Dd,{agent:T},T.id))}),o.length>0&&i.jsxs("details",{className:"mt-3",children:[i.jsxs("summary",{className:"text-xs text-slate cursor-pointer hover:text-charcoal transition-colors",children:[o.length," completed"]}),i.jsx("div",{className:"space-y-1.5 mt-2",children:o.slice(0,10).map(T=>i.jsx(Dd,{agent:T,compact:!0},T.id))})]})]})}function Dd({agent:f,compact:g}){const p=O0(f.type||f.name);return g?i.jsxs("div",{className:"flex items-center gap-2 px-2 py-1 rounded-lg bg-white/30 text-xs",children:[i.jsx("div",{className:"w-1.5 h-1.5 rounded-full bg-slate/30"}),i.jsx("span",{className:"font-medium text-slate truncate",children:f.name||f.id}),i.jsx("span",{className:"text-primary-wash ml-auto",children:f.type})]}):i.jsxs("div",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border ${p}`,children:[i.jsx("div",{className:"flex-shrink-0 mt-0.5",children:i.jsx("div",{className:`w-2.5 h-2.5 rounded-full ${f.alive?"bg-success phase-active":"bg-slate/30"}`})}),i.jsxs("div",{className:"flex-1 min-w-0",children:[i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsx("span",{className:"text-sm font-semibold truncate",children:f.name||f.id}),f.type&&i.jsx("span",{className:"text-[10px] font-mono font-medium opacity-70",children:f.type})]}),f.task&&i.jsx("p",{className:"text-xs opacity-70 mt-0.5 truncate",children:f.task}),f.status&&f.status!=="unknown"&&i.jsx("span",{className:"inline-block text-[10px] font-mono mt-1 opacity-60",children:f.status})]}),f.pid&&i.jsxs("span",{className:"text-[10px] font-mono text-slate/50 flex-shrink-0",children:["PID ",f.pid]})]})}const U0={info:"text-primary-light",error:"text-danger",warning:"text-warning",debug:"text-slate",critical:"text-danger font-bold"};function C0(f){if(!f)return"";if(f.includes("T")||f.includes("-"))try{return new Date(f).toLocaleTimeString("en-US",{hour12:!1})}catch{return f}return f}function R0({logs:f,loading:g,subscribe:p}){const o=U.useRef(null),[T,M]=U.useState(!1),[R,L]=U.useState([]);U.useEffect(()=>p?p("log",Q=>{const ll=Q;ll!=null&&ll.line&&L(al=>{const F=[...al,{message:ll.line,timestamp:ll.timestamp||""}];return F.length>500?F.slice(-500):F})}):void 0,[p]);const D=(()=>{const O=R.map(F=>{let Nl="info";const xl=F.message.toLowerCase();return xl.includes("error")||xl.includes("fail")?Nl="error":xl.includes("warn")?Nl="warning":xl.includes("debug")&&(Nl="debug"),{timestamp:F.timestamp,level:Nl,message:F.message,source:"ws"}}),Q=f||[];if(O.length===0)return Q;if(Q.length===0)return O;const ll=new Set(O.map(F=>F.message));return[...Q.filter(F=>!ll.has(F.message)),...O]})();U.useEffect(()=>{!T&&o.current&&(o.current.scrollTop=o.current.scrollHeight)},[D,T]);const N=()=>{if(!o.current)return;const{scrollTop:O,scrollHeight:Q,clientHeight:ll}=o.current,al=Q-O-ll<50;M(!al)},q=()=>{var O;M(!1),(O=o.current)==null||O.scrollTo({top:o.current.scrollHeight,behavior:"smooth"})};return i.jsxs("div",{className:"glass p-0 overflow-hidden flex flex-col h-full",children:[i.jsxs("div",{className:"flex items-center justify-between px-4 py-3 border-b border-white/10",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Terminal"}),i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsxs("span",{className:"font-mono text-xs text-slate",children:[D.length," lines"]}),i.jsx("button",{onClick:T?q:()=>M(!0),className:`text-xs font-medium px-2.5 py-1 rounded-lg border transition-colors ${T?"border-warning/40 text-warning bg-warning/5 hover:bg-warning/10":"border-primary/20 text-primary hover:bg-primary/5"}`,title:T?"Scroll locked -- click to resume auto-scroll":"Auto-scrolling -- click to lock",children:T?"Locked":"Live"}),T&&i.jsx("button",{onClick:q,className:"text-xs text-primary hover:text-primary-light transition-colors font-medium",children:"Jump to bottom"})]})]}),i.jsxs("div",{ref:o,onScroll:N,className:"flex-1 overflow-y-auto terminal-scroll bg-charcoal/[0.03] p-4 font-mono text-xs leading-relaxed min-h-[300px]",children:[g&&!f&&R.length===0&&i.jsx("div",{className:"text-slate animate-pulse",children:"Connecting to log stream..."}),D.length===0&&!g&&i.jsxs("div",{className:"text-slate/60",children:[i.jsx("p",{children:"No log output yet."}),i.jsx("p",{className:"mt-1",children:"Start a build to see terminal output here."})]}),D.map((O,Q)=>i.jsxs("div",{className:"flex gap-2 hover:bg-white/5 rounded px-1 -mx-1",children:[i.jsx("span",{className:"text-slate/40 flex-shrink-0 select-none w-16 text-right",children:C0(O.timestamp)}),i.jsx("span",{className:`flex-shrink-0 w-12 text-right uppercase text-[10px] font-semibold ${U0[O.level]||"text-slate"}`,children:O.level}),i.jsx("span",{className:`flex-1 break-all ${O.level==="error"||O.level==="critical"?"text-danger":"text-charcoal/80"}`,children:O.message})]},Q)),D.length>0&&i.jsx("div",{className:"terminal-cursor mt-1"})]})]})}const H0={pass:{badge:"bg-success/10 text-success border-success/20",dot:"bg-success",label:"Pass"},fail:{badge:"bg-danger/10 text-danger border-danger/20",dot:"bg-danger",label:"Fail"},skip:{badge:"bg-slate/10 text-slate border-slate/20",dot:"bg-slate/40",label:"Skip"},pending:{badge:"bg-warning/10 text-warning border-warning/20",dot:"bg-warning",label:"Pending"}};function B0({item:f}){const[g,p]=U.useState(!1),o=H0[f.status];return i.jsxs("div",{className:`border rounded-xl overflow-hidden ${o.badge}`,children:[i.jsxs("button",{type:"button",className:"w-full flex items-center gap-3 px-3 py-2.5 text-left",onClick:()=>f.details&&p(!g),children:[i.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${o.dot}`}),i.jsx("span",{className:"text-sm font-medium flex-1 truncate",children:f.label}),i.jsx("span",{className:"text-[10px] font-mono font-semibold uppercase tracking-wider flex-shrink-0",children:o.label}),f.details&&i.jsx("span",{className:"text-xs text-slate/60 flex-shrink-0",children:g?"v":">"})]}),g&&f.details&&i.jsx("div",{className:"px-3 pb-2.5 pt-0",children:i.jsx("p",{className:"text-xs font-mono opacity-70 leading-relaxed",children:f.details})})]})}function q0({checklist:f,loading:g}){const p=f&&f.total>0?f.passed/f.total*100:0;return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Quality Gates"}),f&&i.jsxs("span",{className:"font-mono text-xs text-slate",children:[f.passed,"/",f.total," passed"]})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading gates..."}),!g&&!f&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No quality gate data"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Gates run during verification phase"})]}),f&&i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"flex items-center gap-4 mb-3 text-xs",children:[i.jsxs("span",{className:"text-success font-medium",children:[f.passed," passed"]}),f.failed>0&&i.jsxs("span",{className:"text-danger font-medium",children:[f.failed," failed"]}),f.skipped>0&&i.jsxs("span",{className:"text-slate",children:[f.skipped," skipped"]}),f.pending>0&&i.jsxs("span",{className:"text-warning",children:[f.pending," pending"]})]}),i.jsx("div",{className:"w-full h-2 bg-charcoal/10 rounded-full overflow-hidden mb-4",children:i.jsx("div",{className:"h-full bg-success rounded-full transition-all duration-500",style:{width:`${p}%`}})}),i.jsx("div",{className:"space-y-2 max-h-[400px] overflow-y-auto terminal-scroll",children:f.items.map(o=>i.jsx(B0,{item:o},o.id))})]})]})}const Y0={".py":"bg-success",".ts":"bg-primary",".tsx":"bg-primary",".md":"bg-warning",".sh":"bg-accent-product"};function G0(f){const g=f.substring(f.lastIndexOf("."));return Y0[g]||"bg-slate"}function w0(f){return f==null?"":f<1024?`${f}B`:f<1024*1024?`${(f/1024).toFixed(1)}KB`:`${(f/(1024*1024)).toFixed(1)}MB`}function Hd({node:f,depth:g,onSelectFile:p,selectedPath:o}){const[T,M]=U.useState(g===0),R=f.type==="directory",L=f.path===o;return i.jsxs("div",{children:[i.jsxs("button",{type:"button",className:`w-full flex items-center gap-2 px-2 py-1 rounded-lg text-left text-sm transition-colors hover:bg-white/30 ${L?"bg-primary/10 text-primary":"text-charcoal"}`,style:{paddingLeft:`${g*16+8}px`},onClick:()=>{R?M(!T):p(f.path)},children:[R?i.jsx("span",{className:"font-mono text-xs text-slate w-3 flex-shrink-0",children:T?"v":">"}):i.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${G0(f.name)}`}),i.jsx("span",{className:`truncate ${R?"font-medium":"font-mono text-xs"}`,children:f.name}),!R&&f.size!==void 0&&i.jsx("span",{className:"ml-auto text-[10px] font-mono text-slate/60 flex-shrink-0",children:w0(f.size)})]}),R&&T&&f.children&&i.jsx("div",{children:f.children.map(D=>i.jsx(Hd,{node:D,depth:g+1,onSelectFile:p,selectedPath:o},D.path))})]})}function Q0({files:f,loading:g}){const[p,o]=U.useState(null),[T,M]=U.useState(null),[R,L]=U.useState(!1),[D,N]=U.useState(null),q=U.useCallback(async Q=>{M(null),o(Q),L(!0),N(null);try{const ll=await ql.getFileContent(Q);M(ll.content)}catch(ll){const al=ll instanceof Error?ll.message:"Unknown error",F=ll instanceof TypeError||al==="Request timeout",Nl=al.includes("404")||al.includes("not found")||al.includes("Not found");N(F?"Network error - server may be unreachable":Nl?"File not found - it may have been deleted or renamed":al),M(null)}finally{L(!1)}},[]),O=U.useCallback(Q=>{q(Q)},[q]);return i.jsxs("div",{className:"glass p-6 flex flex-col",style:{minHeight:"300px"},children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"File Browser"}),i.jsx("span",{className:"font-mono text-xs text-slate",children:".loki/"})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading files..."}),!g&&(!f||f.length===0)&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No project files found"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Start a session to generate .loki/ state"})]}),f&&f.length>0&&i.jsxs("div",{className:"flex gap-4 flex-1 min-h-0",children:[i.jsx("div",{className:"w-1/2 overflow-y-auto terminal-scroll pr-2",children:f.map(Q=>i.jsx(Hd,{node:Q,depth:0,onSelectFile:O,selectedPath:p},Q.path))}),i.jsxs("div",{className:"w-1/2 bg-charcoal/5 rounded-xl p-3 overflow-hidden flex flex-col",children:[!p&&i.jsx("div",{className:"flex-1 flex items-center justify-center text-slate text-xs",children:"Select a file to preview"}),p&&i.jsxs(i.Fragment,{children:[i.jsx("div",{className:"text-xs font-mono text-primary mb-2 truncate",children:p}),i.jsx("div",{className:"flex-1 overflow-y-auto terminal-scroll",children:R?i.jsx("div",{className:"text-slate text-xs",children:"Loading..."}):D?i.jsxs("div",{className:"flex flex-col items-center justify-center gap-2 py-6",children:[i.jsx("p",{className:"text-danger text-xs font-medium",children:"Failed to load file"}),i.jsx("p",{className:"text-slate text-[10px] text-center max-w-[200px] break-words",children:D}),i.jsx("button",{type:"button",onClick:()=>p&&q(p),className:"mt-1 px-3 py-1 text-[10px] font-semibold rounded-lg border border-primary/20 text-primary hover:bg-primary/5 transition-colors",children:"Retry"})]}):i.jsx("pre",{className:"text-xs font-mono text-charcoal whitespace-pre-wrap break-words leading-relaxed",children:T})})]})]})]})]})}const Ud=5e5;function Cd(f){return f>=1e6?`${(f/1e6).toFixed(1)}M`:f>=1e3?`${(f/1e3).toFixed(1)}K`:f.toString()}function X0(f){if(!f)return"Never";try{const g=new Date(f),o=new Date().getTime()-g.getTime(),T=Math.floor(o/(1e3*60*60));return T<1?"Just now":T<24?`${T}h ago`:`${Math.floor(T/24)}d ago`}catch{return f}}function L0({memory:f,loading:g}){const p=f?[{label:"Episodic",count:f.episodic_count,color:"text-primary",bg:"bg-primary/10",border:"border-primary/20"},{label:"Semantic",count:f.semantic_count,color:"text-success",bg:"bg-success/10",border:"border-success/20"},{label:"Skills",count:f.skill_count,color:"text-warning",bg:"bg-warning/10",border:"border-warning/20"}]:[],o=f?Math.min(f.total_tokens/Ud*100,100):0;return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Memory System"}),f&&i.jsx("span",{className:"font-mono text-xs text-slate",children:X0(f.last_consolidation)})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading memory..."}),!g&&!f&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No memory data available"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Memory populates during autonomous runs"})]}),f&&i.jsxs(i.Fragment,{children:[i.jsx("div",{className:"grid grid-cols-3 gap-3 mb-4",children:p.map(T=>i.jsxs("div",{className:`${T.bg} border ${T.border} rounded-xl p-3 text-center`,children:[i.jsx("div",{className:`text-2xl font-bold font-mono ${T.color}`,children:T.count}),i.jsx("div",{className:"text-[10px] text-slate font-medium mt-1 uppercase tracking-wider",children:T.label})]},T.label))}),i.jsxs("div",{className:"mt-3",children:[i.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[i.jsx("span",{className:"text-xs text-slate font-medium",children:"Token Usage"}),i.jsxs("span",{className:"text-xs font-mono text-charcoal",children:[Cd(f.total_tokens)," / ",Cd(Ud)]})]}),i.jsx("div",{className:"w-full h-2 bg-charcoal/10 rounded-full overflow-hidden",children:i.jsx("div",{className:`h-full rounded-full transition-all duration-500 ${o>80?"bg-danger":o>50?"bg-warning":"bg-primary-light"}`,style:{width:`${o}%`}})})]}),i.jsxs("div",{className:"mt-3 flex items-center justify-between text-xs",children:[i.jsx("span",{className:"text-slate",children:"Last Consolidation"}),i.jsx("span",{className:"font-mono text-charcoal",children:f.last_consolidation?new Date(f.last_consolidation).toLocaleString():"Never"})]})]})]})}function Z0({visible:f}){const[g,p]=U.useState("markdown"),[o,T]=U.useState(null),[M,R]=U.useState(null),[L,D]=U.useState(!1),[N,q]=U.useState(!1),[O,Q]=U.useState(null),[ll,al]=U.useState(!1);if(!f)return null;const F=async()=>{D(!0),Q(null),T(null),R(null);try{const J=await ql.generateReport(g);T(J)}catch(J){Q(J instanceof Error?J.message:"Failed to generate report")}finally{D(!1)}},Nl=async()=>{q(!0),Q(null);try{const J=await ql.shareSession();R(J)}catch(J){Q(J instanceof Error?J.message:"Failed to share session")}finally{q(!1)}},xl=async J=>{try{await navigator.clipboard.writeText(J),al(!0),setTimeout(()=>al(!1),2e3)}catch{}},gl=()=>{if(!o)return;const J=new Blob([o.content],{type:g==="html"?"text/html":"text/markdown"}),jl=URL.createObjectURL(J),Ol=document.createElement("a");Ol.href=jl,Ol.download=`loki-report.${g==="html"?"html":"md"}`,Ol.click(),URL.revokeObjectURL(jl)};return i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsxs("div",{className:"flex items-center justify-between mb-3",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Session Report"}),i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsx("div",{className:"flex items-center gap-1 glass-subtle rounded-xl p-1",children:["markdown","html"].map(J=>i.jsx("button",{onClick:()=>p(J),className:`px-3 py-1 text-xs font-semibold rounded-lg transition-all ${g===J?"bg-accent-product text-white shadow-sm":"text-slate hover:text-charcoal hover:bg-white/40"}`,children:J.toUpperCase()},J))}),i.jsx("button",{onClick:F,disabled:L,className:"px-4 py-1.5 rounded-xl text-xs font-semibold bg-accent-product text-white hover:bg-accent-product/90 disabled:opacity-50 transition-all",children:L?"Generating...":"Generate Report"})]})]}),O&&i.jsx("div",{className:"mb-3 px-3 py-2 rounded-lg bg-danger/10 border border-danger/20 text-danger text-xs",children:O}),o&&i.jsxs("div",{className:"mt-3",children:[i.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[i.jsxs("span",{className:"text-xs text-slate",children:["Report generated (",o.format,")"]}),i.jsx("div",{className:"flex-1"}),i.jsx("button",{onClick:()=>xl(o.content),className:"px-3 py-1 text-xs font-medium text-slate hover:text-charcoal border border-white/30 rounded-lg hover:bg-white/30 transition-all",children:ll?"Copied":"Copy"}),i.jsx("button",{onClick:gl,className:"px-3 py-1 text-xs font-medium text-slate hover:text-charcoal border border-white/30 rounded-lg hover:bg-white/30 transition-all",children:"Download"}),i.jsx("button",{onClick:Nl,disabled:N,className:"px-3 py-1 text-xs font-medium bg-accent-product/10 text-accent-product border border-accent-product/20 rounded-lg hover:bg-accent-product/20 disabled:opacity-50 transition-all",children:N?"Sharing...":"Share as Gist"})]}),M&&i.jsxs("div",{className:"mb-2 flex items-center gap-2 px-3 py-2 rounded-lg bg-success/10 border border-success/20",children:[i.jsx("span",{className:"text-xs text-success font-medium",children:"Shared:"}),M.url?i.jsx("a",{href:M.url,target:"_blank",rel:"noopener noreferrer",className:"text-xs text-accent-product underline flex-1 truncate",children:M.url}):i.jsx("span",{className:"text-xs text-slate flex-1",children:"No URL returned"}),M.url&&i.jsx("button",{onClick:()=>xl(M.url),className:"text-xs text-slate hover:text-charcoal",children:"Copy URL"})]}),i.jsx("pre",{className:"text-[11px] font-mono text-charcoal bg-black/5 rounded-xl p-3 overflow-auto max-h-64 whitespace-pre-wrap terminal-scroll",children:o.content||"(empty report)"})]})]})}function Rd({visible:f}){const g=U.useCallback(()=>ql.getMetrics(),[]),{data:p,loading:o}=Ra(g,15e3,f);return f?i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsxs("div",{className:"flex items-center justify-between mb-3",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Session Metrics"}),o&&i.jsx("div",{className:"w-4 h-4 border-2 border-accent-product border-t-transparent rounded-full animate-spin"})]}),p?i.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Iterations"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:p.iterations??0})]}),i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Gate Pass Rate"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:typeof p.quality_gate_pass_rate=="number"?`${p.quality_gate_pass_rate.toFixed(0)}%`:"N/A"})]}),i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Tokens Used"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:(p.tokens_used??0).toLocaleString()})]}),i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Time Elapsed"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:p.time_elapsed||"N/A"})]})]}):i.jsx("div",{className:"text-sm text-slate py-4 text-center",children:o?"Loading metrics...":"No metrics available"})]}):null}const V0={completed:{bg:"bg-success/10",text:"text-success",label:"Completed"},complete:{bg:"bg-success/10",text:"text-success",label:"Completed"},done:{bg:"bg-success/10",text:"text-success",label:"Completed"},in_progress:{bg:"bg-primary/10",text:"text-primary",label:"In Progress"},started:{bg:"bg-warning/10",text:"text-warning",label:"Started"},error:{bg:"bg-danger/10",text:"text-danger",label:"Failed"},failed:{bg:"bg-danger/10",text:"text-danger",label:"Failed"},empty:{bg:"bg-slate/10",text:"text-slate",label:"Empty"}};function K0(f){return V0[f]||{bg:"bg-slate/10",text:"text-slate",label:f}}function J0({onLoadSession:f}){const g=U.useCallback(()=>ql.getSessionsHistory(),[]),{data:p,loading:o}=Ra(g,6e4,!0);return o&&!p?i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider mb-3",children:"Past Builds"}),i.jsx("div",{className:"text-sm text-slate",children:"Loading..."})]}):!p||p.length===0?null:i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider mb-3",children:"Past Builds"}),i.jsx("div",{className:"flex flex-col gap-2 max-h-64 overflow-y-auto terminal-scroll",children:p.map(T=>{const M=K0(T.status),R=T.file_count;return i.jsxs("button",{onClick:()=>f==null?void 0:f(T),className:"text-left px-4 py-3 rounded-xl glass-subtle hover:bg-white/40 transition-all group cursor-pointer",children:[i.jsxs("div",{className:"flex items-center justify-between mb-1",children:[i.jsx("span",{className:"text-[10px] font-mono text-slate",children:T.date}),i.jsxs("div",{className:"flex items-center gap-2",children:[R!==void 0&&R>0&&i.jsxs("span",{className:"text-[10px] font-mono text-slate",children:[R," files"]}),i.jsx("span",{className:`text-[10px] font-semibold px-2 py-0.5 rounded-full ${M.bg} ${M.text}`,children:M.label})]})]}),i.jsx("div",{className:"text-xs text-charcoal truncate group-hover:text-accent-product transition-colors",children:T.prd_snippet||T.id}),i.jsx("div",{className:"text-[10px] font-mono text-slate/60 mt-0.5 truncate",children:T.path})]},T.id)})})]})}function k0(){const[f,g]=U.useState(null),[p,o]=U.useState(!1),[T,M]=U.useState(!1),[R,L]=U.useState(null),[D,N]=U.useState(!1),[q,O]=U.useState(!1),[Q,ll]=U.useState(!1),[al,F]=U.useState("terminal"),[Nl,xl]=U.useState("claude"),[gl,J]=U.useState(null),[jl,Ol]=U.useState(null),[Al,k]=U.useState(null),[Yl,wl]=U.useState(null),tt=U.useCallback(pl=>{if(!pl){Ol(null),k(null),wl(null);return}Ol(pl.status),k(pl.agents),wl(pl.logs),o(pl.status.running??!1),M(pl.status.paused??!1)},[]),{connected:Ql,subscribe:Xl}=p0(tt),ft=U.useCallback(()=>ql.getStatus(),[]),{data:Vl}=Ra(ft,3e4,!Ql);U.useEffect(()=>{jl===null&&Vl!==null&&(o(Vl.running??!1),M(Vl.paused??!1))},[Vl,jl]),U.useEffect(()=>{p&&N(!0)},[p]);const Pl=U.useCallback(()=>ql.getMemorySummary(),[]),S=U.useCallback(()=>ql.getChecklist(),[]),_=U.useCallback(()=>ql.getFiles(),[]),{data:G,loading:ul}=Ra(Pl,3e4,p),{data:ol,loading:m}=Ra(S,3e4,p),{data:A,loading:C}=Ra(_,3e4,p),H=jl??Vl,Z=Al,W=Yl,cl=Al===null,Kl=Yl===null,El=U.useCallback(async(pl,Yt,Hn,Ba)=>{g(null),N(!1),O(!1),F("terminal");try{await ql.startSession({prd:pl,provider:Yt,projectDir:Hn,mode:Ba}),L(pl)}catch($e){g($e instanceof Error?$e.message:"Failed to start session")}},[]),_e=U.useCallback(async()=>{try{(await ql.stopSession()).stopped&&(o(!1),M(!1),L(null),Ol(null),k(null),wl(null))}catch{o(!1),M(!1),L(null)}},[]),Je=U.useCallback(async pl=>{try{const Yt=await ql.getSessionDetail(pl.id);J(Yt)}catch{}},[]),ke=U.useCallback(pl=>{xl(pl)},[]),Rn=U.useCallback(async()=>{try{await ql.pauseSession(),M(!0)}catch{}},[]),qt=U.useCallback(async()=>{try{await ql.resumeSession(),M(!1)}catch{}},[]),Ha=R&&R.replace(/^#+\s*/gm,"").split(`
|
|
65
|
+
`)}},[]),Pl=async()=>{if(!(!M.trim()||tt)){Ql(!0),wl(null),ft(!0);try{const _=await ql.planSession(M,ll);wl(_)}catch{wl({complexity:"unknown",cost_estimate:"N/A",iterations:0,phases:[],output_text:"Failed to run loki plan. The CLI may not be available.",returncode:1})}finally{Ql(!1)}}},S=async()=>{if(!(!M.trim()||g||jl)){ft(!1),Ol(!0);try{await f(M,ll,O.trim()||void 0,Al?"quick":void 0)}finally{Ol(!1)}}};return i.jsxs(i.Fragment,{children:[Xl&&i.jsx(z0,{plan:Yl,loading:tt,onConfirm:S,onCancel:()=>ft(!1)}),i.jsxs("div",{className:"glass p-6 flex flex-col",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Product Requirements"}),i.jsx("div",{className:"flex items-center gap-2",children:i.jsxs("div",{className:"relative",children:[i.jsx("button",{onClick:()=>F(!al),className:"text-xs font-medium px-3 py-1.5 rounded-xl border border-primary/20 text-primary hover:bg-primary/5 transition-colors",children:L||"Templates"}),al&&i.jsx("div",{className:"absolute right-0 top-full mt-1 w-56 glass-subtle rounded-xl overflow-hidden z-20 shadow-glass",children:i.jsxs("div",{className:"py-1 max-h-64 overflow-y-auto terminal-scroll",children:[gl&&i.jsx("div",{className:"px-3 py-2 text-xs text-warning border-b border-warning/10",children:"Could not load templates from server. Check that the backend is running."}),!gl&&Nl.length===0&&i.jsx("div",{className:"px-3 py-2 text-xs text-slate",children:"Loading..."}),Nl.map(_=>i.jsx("button",{onClick:()=>Vl(_.filename,_.name),className:"w-full text-left px-3 py-2 text-sm text-charcoal hover:bg-primary/5 transition-colors",children:_.name},_.filename))]})})]})})]}),i.jsx("textarea",{value:M,onChange:_=>R(_.target.value),placeholder:"Paste your PRD here, or select a template above to get started...",className:"flex-1 min-h-[280px] w-full bg-white/40 rounded-xl border border-white/30 px-4 py-3 text-sm font-mono text-charcoal placeholder:text-primary-wash resize-none focus:outline-none focus:ring-2 focus:ring-accent-product/20 focus:border-accent-product/30 transition-all",spellCheck:!1}),i.jsxs("div",{className:"mt-3",children:[i.jsx("label",{className:"block text-xs text-slate font-medium mb-1 uppercase tracking-wider",children:"Project Directory"}),i.jsx("input",{type:"text",value:O,onChange:_=>Q(_.target.value),placeholder:"Leave blank to auto-create, or type a path (e.g. /Users/you/my-project)",className:"w-full bg-white/40 rounded-xl border border-white/30 px-4 py-2 text-sm font-mono text-charcoal placeholder:text-primary-wash/70 focus:outline-none focus:ring-2 focus:ring-accent-product/20 focus:border-accent-product/30 transition-all",spellCheck:!1}),i.jsx("p",{className:"text-[10px] text-slate mt-1",children:"Type a path or leave blank to auto-create under ~/purple-lab-projects/"})]}),p&&i.jsx("div",{className:"mt-3 px-3 py-2 rounded-lg bg-danger/10 border border-danger/20 text-danger text-xs font-medium",children:p}),i.jsxs("div",{className:"flex items-center gap-3 mt-4",children:[i.jsxs("button",{onClick:()=>k(!Al),title:"Quick Mode: 3 iterations max, faster builds",className:`flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-semibold border transition-all ${Al?"bg-accent-product/10 border-accent-product/30 text-accent-product":"border-white/30 text-slate hover:text-charcoal hover:bg-white/20"}`,children:[i.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${Al?"bg-accent-product":"bg-slate/40"}`}),"Quick"]}),i.jsx("div",{className:"flex-1"}),i.jsxs("span",{className:"text-xs text-slate font-mono",children:[M.length.toLocaleString()," chars"]}),i.jsx("button",{onClick:Pl,disabled:!M.trim()||g||tt,className:`px-4 py-2.5 rounded-xl text-sm font-semibold border transition-all ${!M.trim()||g||tt?"border-white/20 text-slate/40 cursor-not-allowed":"border-accent-product/30 text-accent-product hover:bg-accent-product/5"}`,children:tt?"Analyzing...":"Estimate"}),i.jsx("button",{onClick:S,disabled:!M.trim()||g||jl,className:`px-6 py-2.5 rounded-xl text-sm font-semibold transition-all ${!M.trim()||g||jl?"bg-surface/50 text-slate cursor-not-allowed":"bg-accent-product text-white hover:bg-accent-product/90 shadow-glass-subtle"}`,children:jl?"Starting...":g?"Building...":"Start Build"})]})]})]})}const Cn=[{key:"reason",label:"Reason",description:"Analyzing task, planning approach"},{key:"act",label:"Act",description:"Implementing changes, writing code"},{key:"reflect",label:"Reflect",description:"Reviewing output, self-critique"},{key:"verify",label:"Verify",description:"Testing, validation, quality gates"}];function _0(f){const g=f.toLowerCase();return g.includes("reason")||g.includes("plan")?"reason":g.includes("act")||g.includes("implement")||g.includes("code")?"act":g.includes("reflect")||g.includes("review")?"reflect":g.includes("verify")||g.includes("test")||g.includes("check")?"verify":"idle"}function M0({currentPhase:f,iteration:g}){const p=_0(f);return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"RARV Cycle"}),i.jsxs("span",{className:"font-mono text-xs text-slate",children:["Iteration ",g]})]}),i.jsx("div",{className:"flex items-center justify-center mb-5",children:i.jsxs("svg",{viewBox:"0 0 120 120",className:"w-28 h-28",children:[Cn.map((o,T)=>{const M=o.key===p,R=p!=="idle"&&Cn.findIndex(q=>q.key===p)>T,L=(T*90-90)*(Math.PI/180),D=60+40*Math.cos(L),N=60+40*Math.sin(L);return i.jsxs("g",{children:[T<Cn.length-1&&i.jsx("line",{x1:D,y1:N,x2:60+40*Math.cos(((T+1)*90-90)*(Math.PI/180)),y2:60+40*Math.sin(((T+1)*90-90)*(Math.PI/180)),stroke:R?"#3D52A0":"#ADBBDA",strokeWidth:R?2:1,strokeDasharray:R?"none":"4 3"}),T===Cn.length-1&&i.jsx("line",{x1:D,y1:N,x2:60+40*Math.cos(-90*(Math.PI/180)),y2:60+40*Math.sin(-90*(Math.PI/180)),stroke:"#ADBBDA",strokeWidth:1,strokeDasharray:"4 3"}),i.jsx("circle",{cx:D,cy:N,r:M?14:10,fill:M?"#3D52A0":R?"#7091E6":"#EDE8F5",stroke:M?"#6C63FF":R?"#3D52A0":"#ADBBDA",strokeWidth:M?3:1.5,className:M?"phase-active":""}),i.jsx("text",{x:D,y:N+(T===0?-20:T===2?24:0),textAnchor:"middle",className:"text-[9px] font-semibold fill-charcoal",dx:T===1?22:T===3?-22:0,children:o.label[0]})]},o.key)}),i.jsx("text",{x:"60",y:"64",textAnchor:"middle",className:"text-lg font-bold font-mono fill-primary",children:g})]})}),i.jsx("div",{className:"space-y-2",children:Cn.map(o=>{const T=o.key===p;return i.jsxs("div",{className:`flex items-center gap-3 px-3 py-2 rounded-xl transition-all duration-200 ${T?"bg-primary/8 border border-primary/20":"opacity-50"}`,children:[i.jsx("div",{className:`w-2.5 h-2.5 rounded-full flex-shrink-0 ${T?"bg-primary phase-active":"bg-surface"}`}),i.jsxs("div",{children:[i.jsx("span",{className:`text-sm font-semibold ${T?"text-primary":"text-slate"}`,children:o.label}),T&&i.jsx("p",{className:"text-xs text-slate mt-0.5",children:o.description})]})]},o.key)})})]})}const Od={architect:"bg-accent-product/10 text-accent-product border-accent-product/20",developer:"bg-primary/10 text-primary border-primary/20",tester:"bg-success/10 text-success border-success/20",reviewer:"bg-warning/10 text-warning border-warning/20",planner:"bg-primary-wash/20 text-charcoal border-primary-wash/30",default:"bg-surface/30 text-slate border-surface/50"};function O0(f){const g=f.toLowerCase();for(const[p,o]of Object.entries(Od))if(g.includes(p))return o;return Od.default}function D0({agents:f,loading:g}){const p=(f==null?void 0:f.filter(T=>T.alive))||[],o=(f==null?void 0:f.filter(T=>!T.alive))||[];return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Agents"}),i.jsxs("span",{className:"font-mono text-xs text-slate",children:[p.length," active"]})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading agents..."}),!g&&(f==null?void 0:f.length)===0&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No agents running"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Start a build to spawn agents"})]}),p.length>0&&i.jsx("div",{className:"space-y-2 mb-4",children:p.map(T=>i.jsx(Dd,{agent:T},T.id))}),o.length>0&&i.jsxs("details",{className:"mt-3",children:[i.jsxs("summary",{className:"text-xs text-slate cursor-pointer hover:text-charcoal transition-colors",children:[o.length," completed"]}),i.jsx("div",{className:"space-y-1.5 mt-2",children:o.slice(0,10).map(T=>i.jsx(Dd,{agent:T,compact:!0},T.id))})]})]})}function Dd({agent:f,compact:g}){const p=O0(f.type||f.name);return g?i.jsxs("div",{className:"flex items-center gap-2 px-2 py-1 rounded-lg bg-white/30 text-xs",children:[i.jsx("div",{className:"w-1.5 h-1.5 rounded-full bg-slate/30"}),i.jsx("span",{className:"font-medium text-slate truncate",children:f.name||f.id}),i.jsx("span",{className:"text-primary-wash ml-auto",children:f.type})]}):i.jsxs("div",{className:`flex items-start gap-3 px-3 py-2.5 rounded-xl border ${p}`,children:[i.jsx("div",{className:"flex-shrink-0 mt-0.5",children:i.jsx("div",{className:`w-2.5 h-2.5 rounded-full ${f.alive?"bg-success phase-active":"bg-slate/30"}`})}),i.jsxs("div",{className:"flex-1 min-w-0",children:[i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsx("span",{className:"text-sm font-semibold truncate",children:f.name||f.id}),f.type&&i.jsx("span",{className:"text-[10px] font-mono font-medium opacity-70",children:f.type})]}),f.task&&i.jsx("p",{className:"text-xs opacity-70 mt-0.5 truncate",children:f.task}),f.status&&f.status!=="unknown"&&i.jsx("span",{className:"inline-block text-[10px] font-mono mt-1 opacity-60",children:f.status})]}),f.pid&&i.jsxs("span",{className:"text-[10px] font-mono text-slate/50 flex-shrink-0",children:["PID ",f.pid]})]})}const U0={info:"text-primary-light",error:"text-danger",warning:"text-warning",debug:"text-slate",critical:"text-danger font-bold"};function C0(f){if(!f)return"";if(f.includes("T")||f.includes("-"))try{return new Date(f).toLocaleTimeString("en-US",{hour12:!1})}catch{return f}return f}function R0({logs:f,loading:g,subscribe:p}){const o=U.useRef(null),[T,M]=U.useState(!1),[R,L]=U.useState([]);U.useEffect(()=>p?p("log",Q=>{const ll=Q;ll!=null&&ll.line&&L(al=>{const F=[...al,{message:ll.line,timestamp:ll.timestamp||""}];return F.length>500?F.slice(-500):F})}):void 0,[p]);const D=(()=>{const O=R.map(F=>{let Nl="info";const xl=F.message.toLowerCase();return xl.includes("error")||xl.includes("fail")?Nl="error":xl.includes("warn")?Nl="warning":xl.includes("debug")&&(Nl="debug"),{timestamp:F.timestamp,level:Nl,message:F.message,source:"ws"}}),Q=f||[];if(O.length===0)return Q;if(Q.length===0)return O;const ll=new Set(O.map(F=>F.message));return[...Q.filter(F=>!ll.has(F.message)),...O]})();U.useEffect(()=>{!T&&o.current&&(o.current.scrollTop=o.current.scrollHeight)},[D,T]);const N=()=>{if(!o.current)return;const{scrollTop:O,scrollHeight:Q,clientHeight:ll}=o.current,al=Q-O-ll<50;M(!al)},q=()=>{var O;M(!1),(O=o.current)==null||O.scrollTo({top:o.current.scrollHeight,behavior:"smooth"})};return i.jsxs("div",{className:"glass p-0 overflow-hidden flex flex-col h-full",children:[i.jsxs("div",{className:"flex items-center justify-between px-4 py-3 border-b border-white/10",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Terminal"}),i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsxs("span",{className:"font-mono text-xs text-slate",children:[D.length," lines"]}),i.jsx("button",{onClick:T?q:()=>M(!0),className:`text-xs font-medium px-2.5 py-1 rounded-lg border transition-colors ${T?"border-warning/40 text-warning bg-warning/5 hover:bg-warning/10":"border-primary/20 text-primary hover:bg-primary/5"}`,title:T?"Scroll locked -- click to resume auto-scroll":"Auto-scrolling -- click to lock",children:T?"Locked":"Live"}),T&&i.jsx("button",{onClick:q,className:"text-xs text-primary hover:text-primary-light transition-colors font-medium",children:"Jump to bottom"})]})]}),i.jsxs("div",{ref:o,onScroll:N,className:"flex-1 overflow-y-auto terminal-scroll bg-charcoal/[0.03] p-4 font-mono text-xs leading-relaxed min-h-[300px]",children:[g&&!f&&R.length===0&&i.jsx("div",{className:"text-slate animate-pulse",children:"Connecting to log stream..."}),D.length===0&&!g&&i.jsxs("div",{className:"text-slate/60",children:[i.jsx("p",{children:"No log output yet."}),i.jsx("p",{className:"mt-1",children:"Start a build to see terminal output here."})]}),D.map((O,Q)=>i.jsxs("div",{className:"flex gap-2 hover:bg-white/5 rounded px-1 -mx-1",children:[i.jsx("span",{className:"text-slate/40 flex-shrink-0 select-none w-16 text-right",children:C0(O.timestamp)}),i.jsx("span",{className:`flex-shrink-0 w-12 text-right uppercase text-[10px] font-semibold ${U0[O.level]||"text-slate"}`,children:O.level}),i.jsx("span",{className:`flex-1 break-all ${O.level==="error"||O.level==="critical"?"text-danger":"text-charcoal/80"}`,children:O.message})]},Q)),D.length>0&&i.jsx("div",{className:"terminal-cursor mt-1"})]})]})}const H0={pass:{badge:"bg-success/10 text-success border-success/20",dot:"bg-success",label:"Pass"},fail:{badge:"bg-danger/10 text-danger border-danger/20",dot:"bg-danger",label:"Fail"},skip:{badge:"bg-slate/10 text-slate border-slate/20",dot:"bg-slate/40",label:"Skip"},pending:{badge:"bg-warning/10 text-warning border-warning/20",dot:"bg-warning",label:"Pending"}};function B0({item:f}){const[g,p]=U.useState(!1),o=H0[f.status];return i.jsxs("div",{className:`border rounded-xl overflow-hidden ${o.badge}`,children:[i.jsxs("button",{type:"button",className:"w-full flex items-center gap-3 px-3 py-2.5 text-left",onClick:()=>f.details&&p(!g),children:[i.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${o.dot}`}),i.jsx("span",{className:"text-sm font-medium flex-1 truncate",children:f.label}),i.jsx("span",{className:"text-[10px] font-mono font-semibold uppercase tracking-wider flex-shrink-0",children:o.label}),f.details&&i.jsx("span",{className:"text-xs text-slate/60 flex-shrink-0",children:g?"v":">"})]}),g&&f.details&&i.jsx("div",{className:"px-3 pb-2.5 pt-0",children:i.jsx("p",{className:"text-xs font-mono opacity-70 leading-relaxed",children:f.details})})]})}function q0({checklist:f,loading:g}){const p=f&&f.total>0?f.passed/f.total*100:0;return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Quality Gates"}),f&&i.jsxs("span",{className:"font-mono text-xs text-slate",children:[f.passed,"/",f.total," passed"]})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading gates..."}),!g&&!f&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No quality gate data"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Gates run during verification phase"})]}),f&&i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"flex items-center gap-4 mb-3 text-xs",children:[i.jsxs("span",{className:"text-success font-medium",children:[f.passed," passed"]}),f.failed>0&&i.jsxs("span",{className:"text-danger font-medium",children:[f.failed," failed"]}),f.skipped>0&&i.jsxs("span",{className:"text-slate",children:[f.skipped," skipped"]}),f.pending>0&&i.jsxs("span",{className:"text-warning",children:[f.pending," pending"]})]}),i.jsx("div",{className:"w-full h-2 bg-charcoal/10 rounded-full overflow-hidden mb-4",children:i.jsx("div",{className:"h-full bg-success rounded-full transition-all duration-500",style:{width:`${p}%`}})}),i.jsx("div",{className:"space-y-2 max-h-[400px] overflow-y-auto terminal-scroll",children:f.items.map(o=>i.jsx(B0,{item:o},o.id))})]})]})}const Y0={".py":"bg-success",".ts":"bg-primary",".tsx":"bg-primary",".md":"bg-warning",".sh":"bg-accent-product"};function G0(f){const g=f.substring(f.lastIndexOf("."));return Y0[g]||"bg-slate"}function w0(f){return f==null?"":f<1024?`${f}B`:f<1024*1024?`${(f/1024).toFixed(1)}KB`:`${(f/(1024*1024)).toFixed(1)}MB`}function Hd({node:f,depth:g,onSelectFile:p,selectedPath:o}){const[T,M]=U.useState(!1),R=f.type==="directory",L=f.path===o;return i.jsxs("div",{children:[i.jsxs("button",{type:"button",className:`w-full flex items-center gap-2 px-2 py-1 rounded-lg text-left text-sm transition-colors hover:bg-white/30 ${L?"bg-primary/10 text-primary":"text-charcoal"}`,style:{paddingLeft:`${g*16+8}px`},onClick:()=>{R?M(!T):p(f.path)},children:[R?i.jsx("span",{className:"font-mono text-xs text-slate w-3 flex-shrink-0",children:T?"v":">"}):i.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${G0(f.name)}`}),i.jsx("span",{className:`truncate ${R?"font-medium":"font-mono text-xs"}`,children:f.name}),!R&&f.size!==void 0&&i.jsx("span",{className:"ml-auto text-[10px] font-mono text-slate/60 flex-shrink-0",children:w0(f.size)})]}),R&&T&&f.children&&f.children.length>0&&i.jsx("div",{children:f.children.map(D=>i.jsx(Hd,{node:D,depth:g+1,onSelectFile:p,selectedPath:o},D.path))})]})}function Q0({files:f,loading:g}){const[p,o]=U.useState(null),[T,M]=U.useState(null),[R,L]=U.useState(!1),[D,N]=U.useState(null),q=U.useCallback(async Q=>{M(null),o(Q),L(!0),N(null);try{const ll=await ql.getFileContent(Q);M(ll.content)}catch(ll){const al=ll instanceof Error?ll.message:"Unknown error",F=ll instanceof TypeError||al==="Request timeout",Nl=al.includes("404")||al.includes("not found")||al.includes("Not found");N(F?"Network error - server may be unreachable":Nl?"File not found - it may have been deleted or renamed":al),M(null)}finally{L(!1)}},[]),O=U.useCallback(Q=>{q(Q)},[q]);return i.jsxs("div",{className:"glass p-6 flex flex-col",style:{minHeight:"300px"},children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"File Browser"}),i.jsx("span",{className:"font-mono text-xs text-slate",children:".loki/"})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading files..."}),!g&&(!f||f.length===0)&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No project files found"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Start a session to generate .loki/ state"})]}),f&&f.length>0&&i.jsxs("div",{className:"flex gap-4 flex-1 min-h-0",children:[i.jsx("div",{className:"w-1/2 overflow-y-auto terminal-scroll pr-2",children:f.map(Q=>i.jsx(Hd,{node:Q,depth:0,onSelectFile:O,selectedPath:p},Q.path))}),i.jsxs("div",{className:"w-1/2 bg-charcoal/5 rounded-xl p-3 overflow-hidden flex flex-col",children:[!p&&i.jsx("div",{className:"flex-1 flex items-center justify-center text-slate text-xs",children:"Select a file to preview"}),p&&i.jsxs(i.Fragment,{children:[i.jsx("div",{className:"text-xs font-mono text-primary mb-2 truncate",children:p}),i.jsx("div",{className:"flex-1 overflow-y-auto terminal-scroll",children:R?i.jsx("div",{className:"text-slate text-xs",children:"Loading..."}):D?i.jsxs("div",{className:"flex flex-col items-center justify-center gap-2 py-6",children:[i.jsx("p",{className:"text-danger text-xs font-medium",children:"Failed to load file"}),i.jsx("p",{className:"text-slate text-[10px] text-center max-w-[200px] break-words",children:D}),i.jsx("button",{type:"button",onClick:()=>p&&q(p),className:"mt-1 px-3 py-1 text-[10px] font-semibold rounded-lg border border-primary/20 text-primary hover:bg-primary/5 transition-colors",children:"Retry"})]}):i.jsx("pre",{className:"text-xs font-mono text-charcoal whitespace-pre-wrap break-words leading-relaxed",children:T})})]})]})]})]})}const Ud=5e5;function Cd(f){return f>=1e6?`${(f/1e6).toFixed(1)}M`:f>=1e3?`${(f/1e3).toFixed(1)}K`:f.toString()}function X0(f){if(!f)return"Never";try{const g=new Date(f),o=new Date().getTime()-g.getTime(),T=Math.floor(o/(1e3*60*60));return T<1?"Just now":T<24?`${T}h ago`:`${Math.floor(T/24)}d ago`}catch{return f}}function L0({memory:f,loading:g}){const p=f?[{label:"Episodic",count:f.episodic_count,color:"text-primary",bg:"bg-primary/10",border:"border-primary/20"},{label:"Semantic",count:f.semantic_count,color:"text-success",bg:"bg-success/10",border:"border-success/20"},{label:"Skills",count:f.skill_count,color:"text-warning",bg:"bg-warning/10",border:"border-warning/20"}]:[],o=f?Math.min(f.total_tokens/Ud*100,100):0;return i.jsxs("div",{className:"glass p-6",children:[i.jsxs("div",{className:"flex items-center justify-between mb-4",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Memory System"}),f&&i.jsx("span",{className:"font-mono text-xs text-slate",children:X0(f.last_consolidation)})]}),g&&!f&&i.jsx("div",{className:"text-center py-8 text-slate text-sm",children:"Loading memory..."}),!g&&!f&&i.jsxs("div",{className:"text-center py-8",children:[i.jsx("p",{className:"text-slate text-sm",children:"No memory data available"}),i.jsx("p",{className:"text-primary-wash text-xs mt-1",children:"Memory populates during autonomous runs"})]}),f&&i.jsxs(i.Fragment,{children:[i.jsx("div",{className:"grid grid-cols-3 gap-3 mb-4",children:p.map(T=>i.jsxs("div",{className:`${T.bg} border ${T.border} rounded-xl p-3 text-center`,children:[i.jsx("div",{className:`text-2xl font-bold font-mono ${T.color}`,children:T.count}),i.jsx("div",{className:"text-[10px] text-slate font-medium mt-1 uppercase tracking-wider",children:T.label})]},T.label))}),i.jsxs("div",{className:"mt-3",children:[i.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[i.jsx("span",{className:"text-xs text-slate font-medium",children:"Token Usage"}),i.jsxs("span",{className:"text-xs font-mono text-charcoal",children:[Cd(f.total_tokens)," / ",Cd(Ud)]})]}),i.jsx("div",{className:"w-full h-2 bg-charcoal/10 rounded-full overflow-hidden",children:i.jsx("div",{className:`h-full rounded-full transition-all duration-500 ${o>80?"bg-danger":o>50?"bg-warning":"bg-primary-light"}`,style:{width:`${o}%`}})})]}),i.jsxs("div",{className:"mt-3 flex items-center justify-between text-xs",children:[i.jsx("span",{className:"text-slate",children:"Last Consolidation"}),i.jsx("span",{className:"font-mono text-charcoal",children:f.last_consolidation?new Date(f.last_consolidation).toLocaleString():"Never"})]})]})]})}function Z0({visible:f}){const[g,p]=U.useState("markdown"),[o,T]=U.useState(null),[M,R]=U.useState(null),[L,D]=U.useState(!1),[N,q]=U.useState(!1),[O,Q]=U.useState(null),[ll,al]=U.useState(!1);if(!f)return null;const F=async()=>{D(!0),Q(null),T(null),R(null);try{const J=await ql.generateReport(g);T(J)}catch(J){Q(J instanceof Error?J.message:"Failed to generate report")}finally{D(!1)}},Nl=async()=>{q(!0),Q(null);try{const J=await ql.shareSession();R(J)}catch(J){Q(J instanceof Error?J.message:"Failed to share session")}finally{q(!1)}},xl=async J=>{try{await navigator.clipboard.writeText(J),al(!0),setTimeout(()=>al(!1),2e3)}catch{}},gl=()=>{if(!o)return;const J=new Blob([o.content],{type:g==="html"?"text/html":"text/markdown"}),jl=URL.createObjectURL(J),Ol=document.createElement("a");Ol.href=jl,Ol.download=`loki-report.${g==="html"?"html":"md"}`,Ol.click(),URL.revokeObjectURL(jl)};return i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsxs("div",{className:"flex items-center justify-between mb-3",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Session Report"}),i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsx("div",{className:"flex items-center gap-1 glass-subtle rounded-xl p-1",children:["markdown","html"].map(J=>i.jsx("button",{onClick:()=>p(J),className:`px-3 py-1 text-xs font-semibold rounded-lg transition-all ${g===J?"bg-accent-product text-white shadow-sm":"text-slate hover:text-charcoal hover:bg-white/40"}`,children:J.toUpperCase()},J))}),i.jsx("button",{onClick:F,disabled:L,className:"px-4 py-1.5 rounded-xl text-xs font-semibold bg-accent-product text-white hover:bg-accent-product/90 disabled:opacity-50 transition-all",children:L?"Generating...":"Generate Report"})]})]}),O&&i.jsx("div",{className:"mb-3 px-3 py-2 rounded-lg bg-danger/10 border border-danger/20 text-danger text-xs",children:O}),o&&i.jsxs("div",{className:"mt-3",children:[i.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[i.jsxs("span",{className:"text-xs text-slate",children:["Report generated (",o.format,")"]}),i.jsx("div",{className:"flex-1"}),i.jsx("button",{onClick:()=>xl(o.content),className:"px-3 py-1 text-xs font-medium text-slate hover:text-charcoal border border-white/30 rounded-lg hover:bg-white/30 transition-all",children:ll?"Copied":"Copy"}),i.jsx("button",{onClick:gl,className:"px-3 py-1 text-xs font-medium text-slate hover:text-charcoal border border-white/30 rounded-lg hover:bg-white/30 transition-all",children:"Download"}),i.jsx("button",{onClick:Nl,disabled:N,className:"px-3 py-1 text-xs font-medium bg-accent-product/10 text-accent-product border border-accent-product/20 rounded-lg hover:bg-accent-product/20 disabled:opacity-50 transition-all",children:N?"Sharing...":"Share as Gist"})]}),M&&i.jsxs("div",{className:"mb-2 flex items-center gap-2 px-3 py-2 rounded-lg bg-success/10 border border-success/20",children:[i.jsx("span",{className:"text-xs text-success font-medium",children:"Shared:"}),M.url?i.jsx("a",{href:M.url,target:"_blank",rel:"noopener noreferrer",className:"text-xs text-accent-product underline flex-1 truncate",children:M.url}):i.jsx("span",{className:"text-xs text-slate flex-1",children:"No URL returned"}),M.url&&i.jsx("button",{onClick:()=>xl(M.url),className:"text-xs text-slate hover:text-charcoal",children:"Copy URL"})]}),i.jsx("pre",{className:"text-[11px] font-mono text-charcoal bg-black/5 rounded-xl p-3 overflow-auto max-h-64 whitespace-pre-wrap terminal-scroll",children:o.content||"(empty report)"})]})]})}function Rd({visible:f}){const g=U.useCallback(()=>ql.getMetrics(),[]),{data:p,loading:o}=Ra(g,15e3,f);return f?i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsxs("div",{className:"flex items-center justify-between mb-3",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:"Session Metrics"}),o&&i.jsx("div",{className:"w-4 h-4 border-2 border-accent-product border-t-transparent rounded-full animate-spin"})]}),p?i.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Iterations"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:p.iterations??0})]}),i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Gate Pass Rate"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:typeof p.quality_gate_pass_rate=="number"?`${p.quality_gate_pass_rate.toFixed(0)}%`:"N/A"})]}),i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Tokens Used"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:(p.tokens_used??0).toLocaleString()})]}),i.jsxs("div",{className:"glass-subtle rounded-xl p-3",children:[i.jsx("div",{className:"text-[10px] font-semibold text-slate uppercase tracking-wider mb-1",children:"Time Elapsed"}),i.jsx("div",{className:"text-xl font-bold text-charcoal",children:p.time_elapsed||"N/A"})]})]}):i.jsx("div",{className:"text-sm text-slate py-4 text-center",children:o?"Loading metrics...":"No metrics available"})]}):null}const V0={completed:{bg:"bg-success/10",text:"text-success",label:"Completed"},complete:{bg:"bg-success/10",text:"text-success",label:"Completed"},done:{bg:"bg-success/10",text:"text-success",label:"Completed"},in_progress:{bg:"bg-primary/10",text:"text-primary",label:"In Progress"},started:{bg:"bg-warning/10",text:"text-warning",label:"Started"},error:{bg:"bg-danger/10",text:"text-danger",label:"Failed"},failed:{bg:"bg-danger/10",text:"text-danger",label:"Failed"},empty:{bg:"bg-slate/10",text:"text-slate",label:"Empty"}};function K0(f){return V0[f]||{bg:"bg-slate/10",text:"text-slate",label:f}}function J0({onLoadSession:f}){const g=U.useCallback(()=>ql.getSessionsHistory(),[]),{data:p,loading:o}=Ra(g,6e4,!0);return o&&!p?i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider mb-3",children:"Past Builds"}),i.jsx("div",{className:"text-sm text-slate",children:"Loading..."})]}):!p||p.length===0?null:i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsx("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider mb-3",children:"Past Builds"}),i.jsx("div",{className:"flex flex-col gap-2 max-h-64 overflow-y-auto terminal-scroll",children:p.map(T=>{const M=K0(T.status),R=T.file_count;return i.jsxs("button",{onClick:()=>f==null?void 0:f(T),className:"text-left px-4 py-3 rounded-xl glass-subtle hover:bg-white/40 transition-all group cursor-pointer",children:[i.jsxs("div",{className:"flex items-center justify-between mb-1",children:[i.jsx("span",{className:"text-[10px] font-mono text-slate",children:T.date}),i.jsxs("div",{className:"flex items-center gap-2",children:[R!==void 0&&R>0&&i.jsxs("span",{className:"text-[10px] font-mono text-slate",children:[R," files"]}),i.jsx("span",{className:`text-[10px] font-semibold px-2 py-0.5 rounded-full ${M.bg} ${M.text}`,children:M.label})]})]}),i.jsx("div",{className:"text-xs text-charcoal truncate group-hover:text-accent-product transition-colors",children:T.prd_snippet||T.id}),i.jsx("div",{className:"text-[10px] font-mono text-slate/60 mt-0.5 truncate",children:T.path})]},T.id)})})]})}function k0(){const[f,g]=U.useState(null),[p,o]=U.useState(!1),[T,M]=U.useState(!1),[R,L]=U.useState(null),[D,N]=U.useState(!1),[q,O]=U.useState(!1),[Q,ll]=U.useState(!1),[al,F]=U.useState("terminal"),[Nl,xl]=U.useState("claude"),[gl,J]=U.useState(null),[jl,Ol]=U.useState(null),[Al,k]=U.useState(null),[Yl,wl]=U.useState(null),tt=U.useCallback(pl=>{if(!pl){Ol(null),k(null),wl(null);return}Ol(pl.status),k(pl.agents),wl(pl.logs),o(pl.status.running??!1),M(pl.status.paused??!1)},[]),{connected:Ql,subscribe:Xl}=p0(tt),ft=U.useCallback(()=>ql.getStatus(),[]),{data:Vl}=Ra(ft,3e4,!Ql);U.useEffect(()=>{jl===null&&Vl!==null&&(o(Vl.running??!1),M(Vl.paused??!1))},[Vl,jl]),U.useEffect(()=>{p&&N(!0)},[p]);const Pl=U.useCallback(()=>ql.getMemorySummary(),[]),S=U.useCallback(()=>ql.getChecklist(),[]),_=U.useCallback(()=>ql.getFiles(),[]),{data:G,loading:ul}=Ra(Pl,3e4,p),{data:ol,loading:m}=Ra(S,3e4,p),{data:A,loading:C}=Ra(_,3e4,p),H=jl??Vl,Z=Al,W=Yl,cl=Al===null,Kl=Yl===null,El=U.useCallback(async(pl,Yt,Hn,Ba)=>{g(null),N(!1),O(!1),F("terminal");try{await ql.startSession({prd:pl,provider:Yt,projectDir:Hn,mode:Ba}),L(pl)}catch($e){g($e instanceof Error?$e.message:"Failed to start session")}},[]),_e=U.useCallback(async()=>{try{(await ql.stopSession()).stopped&&(o(!1),M(!1),L(null),Ol(null),k(null),wl(null))}catch{o(!1),M(!1),L(null)}},[]),Je=U.useCallback(async pl=>{try{const Yt=await ql.getSessionDetail(pl.id);J(Yt)}catch{}},[]),ke=U.useCallback(pl=>{xl(pl)},[]),Rn=U.useCallback(async()=>{try{await ql.pauseSession(),M(!0)}catch{}},[]),qt=U.useCallback(async()=>{try{await ql.resumeSession(),M(!1)}catch{}},[]),Ha=R&&R.replace(/^#+\s*/gm,"").split(`
|
|
66
66
|
`).find(pl=>pl.trim().length>0)||null;return i.jsxs("div",{className:"min-h-screen bg-background relative",children:[i.jsx("div",{className:"pattern-circles"}),i.jsx(S0,{status:H,wsConnected:Ql,onProviderChange:ke,selectedProvider:Nl}),i.jsx("main",{className:"max-w-[1920px] mx-auto px-6 py-6 relative z-10",children:p?i.jsxs(i.Fragment,{children:[i.jsx(T0,{status:H,prdSummary:Ha,onStop:_e,onPause:Rn,onResume:qt,isPaused:T}),i.jsx("div",{className:"mt-4",children:i.jsx(E0,{status:H})}),i.jsxs("div",{className:"mt-4 grid grid-cols-12 gap-6",style:{minHeight:"calc(100vh - 340px)"},children:[i.jsx("div",{className:"col-span-3 flex flex-col gap-6",children:i.jsx(M0,{currentPhase:(H==null?void 0:H.phase)||"idle",iteration:(H==null?void 0:H.iteration)||0})}),i.jsxs("div",{className:"col-span-5 flex flex-col gap-0",children:[i.jsxs("div",{className:"flex items-center gap-1 mb-2",children:[i.jsx("button",{onClick:()=>F("terminal"),className:`px-3 py-1.5 text-xs font-semibold rounded-lg transition-all ${al==="terminal"?"bg-accent-product text-white":"text-slate hover:text-charcoal hover:bg-white/30"}`,children:"Terminal"}),i.jsx("button",{onClick:()=>F("metrics"),className:`px-3 py-1.5 text-xs font-semibold rounded-lg transition-all ${al==="metrics"?"bg-accent-product text-white":"text-slate hover:text-charcoal hover:bg-white/30"}`,children:"Metrics"})]}),al==="terminal"?i.jsx(R0,{logs:W,loading:Kl,subscribe:Xl}):i.jsx(Rd,{visible:!0})]}),i.jsxs("div",{className:"col-span-4 flex flex-col gap-6",children:[i.jsx(D0,{agents:Z,loading:cl}),i.jsx(q0,{checklist:ol,loading:m})]})]}),i.jsxs("div",{className:"mt-6 grid grid-cols-12 gap-6",children:[i.jsx("div",{className:"col-span-6",children:i.jsx(Q0,{files:A,loading:C})}),i.jsx("div",{className:"col-span-6",children:i.jsx(L0,{memory:G,loading:ul})})]})]}):i.jsxs("div",{className:"flex flex-col items-center",children:[i.jsxs("div",{className:"text-center mt-8 mb-8",children:[i.jsx("h2",{className:"text-3xl font-bold text-charcoal tracking-tight",children:"Describe it. Build it. Ship it."}),i.jsx("p",{className:"text-slate mt-2 text-base max-w-lg mx-auto",children:"Paste a PRD or pick a template. Purple Lab spins up autonomous agents to build your project from scratch."})]}),i.jsx("div",{className:"w-full max-w-3xl",children:i.jsx(A0,{onSubmit:El,running:p,error:f,provider:Nl,onProviderChange:ke})}),D&&!p&&i.jsxs("div",{className:"w-full max-w-3xl mt-4 flex flex-col gap-4",children:[i.jsxs("div",{className:"flex items-center gap-3",children:[i.jsx("button",{onClick:()=>O(!q),className:"px-4 py-2 rounded-xl text-sm font-semibold border border-accent-product/30 text-accent-product hover:bg-accent-product/5 transition-all",children:q?"Hide Report Panel":"Report"}),i.jsx("button",{onClick:()=>ll(!Q),className:"px-4 py-2 rounded-xl text-sm font-semibold border border-white/30 text-slate hover:text-charcoal hover:bg-white/30 transition-all",children:Q?"Hide Metrics":"View Metrics"})]}),i.jsx(Z0,{visible:q}),i.jsx(Rd,{visible:Q})]}),gl&&i.jsx("div",{className:"w-full max-w-3xl mt-4",children:i.jsxs("div",{className:"glass p-4 rounded-2xl",children:[i.jsxs("div",{className:"flex items-center justify-between mb-3",children:[i.jsxs("h3",{className:"text-sm font-semibold text-charcoal uppercase tracking-wider",children:["Session: ",gl.id]}),i.jsx("button",{onClick:()=>J(null),className:"text-xs text-slate hover:text-charcoal transition-colors px-2 py-1 rounded-lg hover:bg-white/30",children:"Close"})]}),i.jsx("div",{className:"text-[10px] font-mono text-slate mb-3",children:gl.path}),gl.prd&&i.jsxs("div",{className:"mb-3",children:[i.jsx("div",{className:"text-[10px] text-slate uppercase tracking-wider font-semibold mb-1",children:"PRD"}),i.jsxs("div",{className:"bg-charcoal/[0.03] rounded-lg p-3 font-mono text-xs text-charcoal/80 max-h-32 overflow-y-auto terminal-scroll whitespace-pre-wrap",children:[gl.prd.slice(0,500),gl.prd.length>500?"...":""]})]}),gl.files.length>0&&i.jsxs("div",{className:"mb-3",children:[i.jsxs("div",{className:"text-[10px] text-slate uppercase tracking-wider font-semibold mb-1",children:["Files (",gl.files.length,")"]}),i.jsx("div",{className:"bg-charcoal/[0.03] rounded-lg p-3 font-mono text-xs max-h-40 overflow-y-auto terminal-scroll",children:gl.files.map((pl,Yt)=>i.jsx("div",{className:"text-charcoal/70",children:pl.type==="directory"?`${pl.name}/`:pl.name},Yt))})]}),gl.logs.length>0&&i.jsxs("div",{children:[i.jsxs("div",{className:"text-[10px] text-slate uppercase tracking-wider font-semibold mb-1",children:["Logs (",gl.logs.length," lines)"]}),i.jsx("div",{className:"bg-charcoal/[0.03] rounded-lg p-3 font-mono text-[10px] max-h-40 overflow-y-auto terminal-scroll",children:gl.logs.map((pl,Yt)=>i.jsx("div",{className:"text-charcoal/70",children:pl},Yt))})]})]})}),i.jsx("div",{className:"w-full max-w-3xl mt-4",children:i.jsx(J0,{onLoadSession:Je})}),i.jsxs("div",{className:"mt-6 text-xs text-slate flex items-center gap-2",children:[i.jsx("div",{className:`w-2 h-2 rounded-full ${Ql?"bg-success":"bg-danger"}`}),Ql?"Connected to Purple Lab backend":"Waiting for backend connection..."]})]})})]})}v0.createRoot(document.getElementById("root")).render(i.jsx(U.StrictMode,{children:i.jsx(k0,{})}));
|
package/web-app/dist/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-Bwj6Ubaa.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-CDiM5Vh4.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body class="bg-background text-charcoal font-sans antialiased">
|