loki-mode 6.66.0 → 6.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +3 -2
- package/VERSION +1 -1
- package/autonomy/hooks/migration-hooks.sh +170 -0
- package/autonomy/loki +1018 -1
- package/autonomy/notification-checker.py +4 -3
- package/autonomy/run.sh +22 -10
- package/autonomy/tui.sh +471 -0
- package/completions/_loki +57 -0
- package/completions/loki.bash +39 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +20 -6
- package/docs/INSTALLATION.md +1 -1
- package/events/bus.py +9 -1
- package/events/emit.sh +2 -2
- package/mcp/__init__.py +1 -1
- package/memory/namespace.py +14 -11
- package/memory/schemas.py +155 -0
- package/memory/storage.py +29 -4
- package/package.json +1 -1
- package/references/legacy-healing-patterns.md +352 -0
- package/skills/00-index.md +13 -1
- package/skills/healing.md +491 -0
- package/skills/quality-gates.md +31 -2
- package/skills/troubleshooting.md +33 -0
|
@@ -320,12 +320,13 @@ def check_compaction_frequency(trigger, loki_dir, iteration, notifications):
|
|
|
320
320
|
return None
|
|
321
321
|
|
|
322
322
|
max_per_hour = trigger.get("max_per_hour", 3)
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
one_hour_ago_str = datetime.fromtimestamp(
|
|
324
|
+
time.time() - 3600, tz=timezone.utc
|
|
325
|
+
).isoformat()
|
|
325
326
|
|
|
326
327
|
recent = [
|
|
327
328
|
c for c in compactions
|
|
328
|
-
if c.get("timestamp", "")
|
|
329
|
+
if c.get("timestamp", "") >= one_hour_ago_str
|
|
329
330
|
]
|
|
330
331
|
|
|
331
332
|
if len(recent) >= max_per_hour:
|
package/autonomy/run.sh
CHANGED
|
@@ -1699,7 +1699,10 @@ import_github_issues() {
|
|
|
1699
1699
|
temp_file=$(mktemp ".loki/queue/pending.json.tmp.XXXXXX")
|
|
1700
1700
|
local lockfile=".loki/queue/.pending.lock"
|
|
1701
1701
|
(
|
|
1702
|
-
flock -w 5 200 2>/dev/null
|
|
1702
|
+
if ! flock -w 5 200 2>/dev/null; then
|
|
1703
|
+
log_warn "Could not acquire queue lock for issue #$number, skipping"
|
|
1704
|
+
exit 1
|
|
1705
|
+
fi
|
|
1703
1706
|
if jq ". += [$task_json]" "$pending_file" > "$temp_file" && mv "$temp_file" "$pending_file"; then
|
|
1704
1707
|
log_info "Imported issue #$number: $title"
|
|
1705
1708
|
task_count=$((task_count + 1))
|
|
@@ -3074,10 +3077,11 @@ invoke_cline() {
|
|
|
3074
3077
|
local prompt="$1"
|
|
3075
3078
|
shift
|
|
3076
3079
|
local model="${LOKI_CLINE_MODEL:-}"
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3080
|
+
if [[ -n "$model" ]]; then
|
|
3081
|
+
cline -y -m "$model" "$prompt" "$@" 2>&1
|
|
3082
|
+
else
|
|
3083
|
+
cline -y "$prompt" "$@" 2>&1
|
|
3084
|
+
fi
|
|
3081
3085
|
}
|
|
3082
3086
|
|
|
3083
3087
|
# Invoke Cline and capture output (for variable assignment)
|
|
@@ -3086,10 +3090,11 @@ invoke_cline_capture() {
|
|
|
3086
3090
|
local prompt="$1"
|
|
3087
3091
|
shift
|
|
3088
3092
|
local model="${LOKI_CLINE_MODEL:-}"
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
+
if [[ -n "$model" ]]; then
|
|
3094
|
+
cline -y -m "$model" "$prompt" "$@" 2>&1
|
|
3095
|
+
else
|
|
3096
|
+
cline -y "$prompt" "$@" 2>&1
|
|
3097
|
+
fi
|
|
3093
3098
|
}
|
|
3094
3099
|
|
|
3095
3100
|
#===============================================================================
|
|
@@ -5711,6 +5716,12 @@ SPECIALISTS = {
|
|
|
5711
5716
|
"focus": "Outdated packages, CVEs, bloat, unused deps, license issues",
|
|
5712
5717
|
"checks": "outdated dependencies, known CVEs, unnecessary imports, dependency bloat, license compatibility, unused packages",
|
|
5713
5718
|
"priority": 3
|
|
5719
|
+
},
|
|
5720
|
+
"legacy-healing-auditor": {
|
|
5721
|
+
"keywords": ["legacy", "heal", "migrate", "cobol", "fortran", "refactor", "modernize", "deprecat", "adapter", "friction", "characterization"],
|
|
5722
|
+
"focus": "Behavioral preservation, friction safety, institutional knowledge retention",
|
|
5723
|
+
"checks": "behavioral change without characterization test, removal of quirky code without friction map check, missing adapter layer for replaced components, institutional knowledge loss (deleted comments, removed error messages), breaking changes to undocumented APIs",
|
|
5724
|
+
"priority": 4
|
|
5714
5725
|
}
|
|
5715
5726
|
}
|
|
5716
5727
|
|
|
@@ -9299,7 +9310,8 @@ run_autonomous() {
|
|
|
9299
9310
|
# Auto-track iteration start (for dashboard task queue)
|
|
9300
9311
|
track_iteration_start "$ITERATION_COUNT" "$prd_path"
|
|
9301
9312
|
|
|
9302
|
-
local prompt
|
|
9313
|
+
local prompt
|
|
9314
|
+
prompt=$(build_prompt "$retry" "$prd_path" "$ITERATION_COUNT")
|
|
9303
9315
|
|
|
9304
9316
|
# BUG #5 fix: Clear LOKI_HUMAN_INPUT in the parent shell after build_prompt
|
|
9305
9317
|
# consumed it. build_prompt runs in a subshell (command substitution), so
|
package/autonomy/tui.sh
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck disable=SC2034 # Variables exported for consumers that source this library
|
|
3
|
+
#===============================================================================
|
|
4
|
+
# Loki Mode TUI Library
|
|
5
|
+
# Rich terminal output: spinners, progress bars, tables, boxes, diffs
|
|
6
|
+
#
|
|
7
|
+
# Inspired by Kiro CLI's TUI (kiro.dev/changelog/cli/1-24)
|
|
8
|
+
# Source: arXiv:2602.22518 (RepoMod-Bench) for progress visualization patterns
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# source "$(dirname "${BASH_SOURCE[0]}")/tui.sh"
|
|
12
|
+
# spinner_start "Analyzing codebase..."
|
|
13
|
+
# spinner_stop
|
|
14
|
+
# progress_bar 75 100 "Files processed"
|
|
15
|
+
# draw_box "Title" "Content here"
|
|
16
|
+
#===============================================================================
|
|
17
|
+
|
|
18
|
+
# Guard against double-sourcing
|
|
19
|
+
[[ -n "${_LOKI_TUI_LOADED:-}" ]] && return 0
|
|
20
|
+
_LOKI_TUI_LOADED=1
|
|
21
|
+
|
|
22
|
+
# Terminal capabilities
|
|
23
|
+
TUI_HAS_COLOR=false
|
|
24
|
+
TUI_COLS=80
|
|
25
|
+
if [ -t 1 ]; then
|
|
26
|
+
TUI_HAS_COLOR=true
|
|
27
|
+
TUI_COLS=$(tput cols 2>/dev/null || echo 80)
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Unicode box drawing (degrade to ASCII if terminal doesn't support)
|
|
31
|
+
if echo -e "\xe2\x94\x80" 2>/dev/null | grep -q "─" 2>/dev/null; then
|
|
32
|
+
BOX_H="─"
|
|
33
|
+
BOX_V="│"
|
|
34
|
+
BOX_TL="┌"
|
|
35
|
+
BOX_TR="┐"
|
|
36
|
+
BOX_BL="└"
|
|
37
|
+
BOX_BR="┘"
|
|
38
|
+
BOX_ML="├"
|
|
39
|
+
BOX_MR="┤"
|
|
40
|
+
BULLET=">"
|
|
41
|
+
CHECK_MARK="[ok]"
|
|
42
|
+
CROSS_MARK="[!!]"
|
|
43
|
+
ARROW_R="->"
|
|
44
|
+
SPINNER_CHARS=("/" "-" "\\" "|")
|
|
45
|
+
BAR_FILL="="
|
|
46
|
+
BAR_EMPTY="-"
|
|
47
|
+
else
|
|
48
|
+
BOX_H="-"
|
|
49
|
+
BOX_V="|"
|
|
50
|
+
BOX_TL="+"
|
|
51
|
+
BOX_TR="+"
|
|
52
|
+
BOX_BL="+"
|
|
53
|
+
BOX_BR="+"
|
|
54
|
+
BOX_ML="+"
|
|
55
|
+
BOX_MR="+"
|
|
56
|
+
BULLET=">"
|
|
57
|
+
CHECK_MARK="[ok]"
|
|
58
|
+
CROSS_MARK="[!!]"
|
|
59
|
+
ARROW_R="->"
|
|
60
|
+
SPINNER_CHARS=("/" "-" "\\" "|")
|
|
61
|
+
BAR_FILL="="
|
|
62
|
+
BAR_EMPTY="-"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Colors (only if terminal supports)
|
|
66
|
+
if $TUI_HAS_COLOR; then
|
|
67
|
+
TUI_RED='\033[0;31m'
|
|
68
|
+
TUI_GREEN='\033[0;32m'
|
|
69
|
+
TUI_YELLOW='\033[1;33m'
|
|
70
|
+
TUI_BLUE='\033[0;34m'
|
|
71
|
+
TUI_CYAN='\033[0;36m'
|
|
72
|
+
TUI_MAGENTA='\033[0;35m'
|
|
73
|
+
TUI_BOLD='\033[1m'
|
|
74
|
+
TUI_DIM='\033[2m'
|
|
75
|
+
TUI_NC='\033[0m'
|
|
76
|
+
TUI_UNDERLINE='\033[4m'
|
|
77
|
+
TUI_REVERSE='\033[7m'
|
|
78
|
+
# Model-specific colors (matching dashboard theme)
|
|
79
|
+
TUI_OPUS='\033[0;33m' # amber
|
|
80
|
+
TUI_SONNET='\033[0;35m' # purple
|
|
81
|
+
TUI_HAIKU='\033[0;36m' # teal
|
|
82
|
+
else
|
|
83
|
+
TUI_RED='' TUI_GREEN='' TUI_YELLOW='' TUI_BLUE='' TUI_CYAN=''
|
|
84
|
+
TUI_MAGENTA='' TUI_BOLD='' TUI_DIM='' TUI_NC='' TUI_UNDERLINE=''
|
|
85
|
+
TUI_REVERSE='' TUI_OPUS='' TUI_SONNET='' TUI_HAIKU=''
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
#--- Spinner -------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
_SPINNER_PID=""
|
|
91
|
+
_SPINNER_MSG=""
|
|
92
|
+
|
|
93
|
+
spinner_start() {
|
|
94
|
+
local msg="${1:-Working...}"
|
|
95
|
+
_SPINNER_MSG="$msg"
|
|
96
|
+
|
|
97
|
+
# Don't spin in non-interactive terminals
|
|
98
|
+
if ! [ -t 1 ]; then
|
|
99
|
+
echo "$msg"
|
|
100
|
+
return
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
(
|
|
104
|
+
local i=0
|
|
105
|
+
while true; do
|
|
106
|
+
local char="${SPINNER_CHARS[$((i % ${#SPINNER_CHARS[@]}))]}"
|
|
107
|
+
printf "\r${TUI_CYAN}%s${TUI_NC} %s" "$char" "$msg" >&2
|
|
108
|
+
sleep 0.1
|
|
109
|
+
i=$((i + 1))
|
|
110
|
+
done
|
|
111
|
+
) &
|
|
112
|
+
_SPINNER_PID=$!
|
|
113
|
+
disown $_SPINNER_PID 2>/dev/null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
spinner_stop() {
|
|
117
|
+
local result="${1:-done}"
|
|
118
|
+
if [ -n "$_SPINNER_PID" ]; then
|
|
119
|
+
kill "$_SPINNER_PID" 2>/dev/null
|
|
120
|
+
wait "$_SPINNER_PID" 2>/dev/null
|
|
121
|
+
_SPINNER_PID=""
|
|
122
|
+
fi
|
|
123
|
+
if [ -t 1 ]; then
|
|
124
|
+
printf "\r${TUI_GREEN}${CHECK_MARK}${TUI_NC} %s ${TUI_DIM}(%s)${TUI_NC}\n" "$_SPINNER_MSG" "$result" >&2
|
|
125
|
+
fi
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
spinner_fail() {
|
|
129
|
+
local reason="${1:-failed}"
|
|
130
|
+
if [ -n "$_SPINNER_PID" ]; then
|
|
131
|
+
kill "$_SPINNER_PID" 2>/dev/null
|
|
132
|
+
wait "$_SPINNER_PID" 2>/dev/null
|
|
133
|
+
_SPINNER_PID=""
|
|
134
|
+
fi
|
|
135
|
+
if [ -t 1 ]; then
|
|
136
|
+
printf "\r${TUI_RED}${CROSS_MARK}${TUI_NC} %s ${TUI_RED}(%s)${TUI_NC}\n" "$_SPINNER_MSG" "$reason" >&2
|
|
137
|
+
fi
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#--- Progress Bar --------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
progress_bar() {
|
|
143
|
+
local current="$1"
|
|
144
|
+
local total="$2"
|
|
145
|
+
local label="${3:-Progress}"
|
|
146
|
+
local width="${4:-40}"
|
|
147
|
+
|
|
148
|
+
if [ "$total" -eq 0 ]; then
|
|
149
|
+
return
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
local pct=$((current * 100 / total))
|
|
153
|
+
local filled=$((current * width / total))
|
|
154
|
+
local empty=$((width - filled))
|
|
155
|
+
|
|
156
|
+
local bar=""
|
|
157
|
+
for ((i=0; i<filled; i++)); do bar+="$BAR_FILL"; done
|
|
158
|
+
for ((i=0; i<empty; i++)); do bar+="$BAR_EMPTY"; done
|
|
159
|
+
|
|
160
|
+
# Color based on percentage
|
|
161
|
+
local color="$TUI_RED"
|
|
162
|
+
if [ "$pct" -ge 80 ]; then color="$TUI_GREEN"
|
|
163
|
+
elif [ "$pct" -ge 50 ]; then color="$TUI_YELLOW"
|
|
164
|
+
elif [ "$pct" -ge 25 ]; then color="$TUI_BLUE"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
printf "\r %s ${color}[%s]${TUI_NC} %3d%% (%d/%d)" "$label" "$bar" "$pct" "$current" "$total"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
progress_bar_done() {
|
|
171
|
+
echo "" # newline after progress bar
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#--- Context Window Gauge ------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
context_gauge() {
|
|
177
|
+
local used="$1" # tokens used
|
|
178
|
+
local total="$2" # context window size
|
|
179
|
+
local label="${3:-Context}"
|
|
180
|
+
local width=30
|
|
181
|
+
|
|
182
|
+
if [ "$total" -eq 0 ]; then return; fi
|
|
183
|
+
|
|
184
|
+
local pct=$((used * 100 / total))
|
|
185
|
+
local filled=$((used * width / total))
|
|
186
|
+
[ "$filled" -gt "$width" ] && filled="$width"
|
|
187
|
+
local empty=$((width - filled))
|
|
188
|
+
|
|
189
|
+
# Color: green < 50%, yellow < 80%, red >= 80%
|
|
190
|
+
local color="$TUI_GREEN"
|
|
191
|
+
if [ "$pct" -ge 80 ]; then color="$TUI_RED"
|
|
192
|
+
elif [ "$pct" -ge 50 ]; then color="$TUI_YELLOW"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
local bar=""
|
|
196
|
+
for ((i=0; i<filled; i++)); do bar+="$BAR_FILL"; done
|
|
197
|
+
for ((i=0; i<empty; i++)); do bar+=" "; done
|
|
198
|
+
|
|
199
|
+
local used_fmt
|
|
200
|
+
local total_fmt
|
|
201
|
+
used_fmt=$(format_tokens "$used")
|
|
202
|
+
total_fmt=$(format_tokens "$total")
|
|
203
|
+
|
|
204
|
+
echo -e " ${TUI_BOLD}$label${TUI_NC} ${color}[${bar}]${TUI_NC} ${pct}% (${used_fmt} / ${total_fmt})"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
format_tokens() {
|
|
208
|
+
local n="$1"
|
|
209
|
+
if [ "$n" -ge 1000000 ]; then
|
|
210
|
+
printf "%.1fM" "$(echo "scale=1; $n / 1000000" | bc 2>/dev/null || echo "$n")"
|
|
211
|
+
elif [ "$n" -ge 1000 ]; then
|
|
212
|
+
printf "%.1fK" "$(echo "scale=1; $n / 1000" | bc 2>/dev/null || echo "$n")"
|
|
213
|
+
else
|
|
214
|
+
echo "$n"
|
|
215
|
+
fi
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#--- Box Drawing ---------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
draw_box() {
|
|
221
|
+
local title="$1"
|
|
222
|
+
shift
|
|
223
|
+
local content="$*"
|
|
224
|
+
local inner_width=$((TUI_COLS - 4))
|
|
225
|
+
[ "$inner_width" -gt 76 ] && inner_width=76
|
|
226
|
+
|
|
227
|
+
# Top border with title
|
|
228
|
+
local title_len=${#title}
|
|
229
|
+
local padding=$((inner_width - title_len - 2))
|
|
230
|
+
[ "$padding" -lt 0 ] && padding=0
|
|
231
|
+
|
|
232
|
+
local top_line="${BOX_TL}"
|
|
233
|
+
top_line+="${BOX_H} ${TUI_BOLD}${title}${TUI_NC} "
|
|
234
|
+
for ((i=0; i<padding; i++)); do top_line+="${BOX_H}"; done
|
|
235
|
+
top_line+="${BOX_TR}"
|
|
236
|
+
echo -e "$top_line"
|
|
237
|
+
|
|
238
|
+
# Content lines
|
|
239
|
+
while IFS= read -r line; do
|
|
240
|
+
local line_plain
|
|
241
|
+
line_plain=$(echo -e "$line" | sed 's/\x1b\[[0-9;]*m//g')
|
|
242
|
+
local line_len=${#line_plain}
|
|
243
|
+
local pad=$((inner_width - line_len))
|
|
244
|
+
[ "$pad" -lt 0 ] && pad=0
|
|
245
|
+
local spaces=""
|
|
246
|
+
for ((i=0; i<pad; i++)); do spaces+=" "; done
|
|
247
|
+
echo -e "${BOX_V} ${line}${spaces} ${BOX_V}"
|
|
248
|
+
done <<< "$content"
|
|
249
|
+
|
|
250
|
+
# Bottom border
|
|
251
|
+
local bottom_line="${BOX_BL}"
|
|
252
|
+
for ((i=0; i<inner_width+2; i++)); do bottom_line+="${BOX_H}"; done
|
|
253
|
+
bottom_line+="${BOX_BR}"
|
|
254
|
+
echo -e "$bottom_line"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#--- Table Rendering -----------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
# Usage: table_render "Col1|Col2|Col3" "val1|val2|val3" "val4|val5|val6"
|
|
260
|
+
table_render() {
|
|
261
|
+
local header="$1"
|
|
262
|
+
shift
|
|
263
|
+
local rows=("$@")
|
|
264
|
+
|
|
265
|
+
# Calculate column widths
|
|
266
|
+
IFS='|' read -ra hcols <<< "$header"
|
|
267
|
+
local ncols=${#hcols[@]}
|
|
268
|
+
local -a widths=()
|
|
269
|
+
for ((i=0; i<ncols; i++)); do
|
|
270
|
+
widths[$i]=${#hcols[$i]}
|
|
271
|
+
done
|
|
272
|
+
|
|
273
|
+
for row in "${rows[@]}"; do
|
|
274
|
+
IFS='|' read -ra rcols <<< "$row"
|
|
275
|
+
for ((i=0; i<ncols; i++)); do
|
|
276
|
+
local cell="${rcols[$i]:-}"
|
|
277
|
+
local clen=${#cell}
|
|
278
|
+
if [ "$clen" -gt "${widths[$i]}" ]; then
|
|
279
|
+
widths[$i]=$clen
|
|
280
|
+
fi
|
|
281
|
+
done
|
|
282
|
+
done
|
|
283
|
+
|
|
284
|
+
# Header
|
|
285
|
+
local hline=" "
|
|
286
|
+
local sep=" "
|
|
287
|
+
for ((i=0; i<ncols; i++)); do
|
|
288
|
+
local w=${widths[$i]}
|
|
289
|
+
hline+=$(printf "${TUI_BOLD}%-${w}s${TUI_NC} " "${hcols[$i]}")
|
|
290
|
+
local dashes=""
|
|
291
|
+
for ((j=0; j<w; j++)); do dashes+="-"; done
|
|
292
|
+
sep+=$(printf "%s " "$dashes")
|
|
293
|
+
done
|
|
294
|
+
echo -e "$hline"
|
|
295
|
+
echo -e "${TUI_DIM}$sep${TUI_NC}"
|
|
296
|
+
|
|
297
|
+
# Rows
|
|
298
|
+
for row in "${rows[@]}"; do
|
|
299
|
+
IFS='|' read -ra rcols <<< "$row"
|
|
300
|
+
local rline=" "
|
|
301
|
+
for ((i=0; i<ncols; i++)); do
|
|
302
|
+
local w=${widths[$i]}
|
|
303
|
+
rline+=$(printf "%-${w}s " "${rcols[$i]:-}")
|
|
304
|
+
done
|
|
305
|
+
echo -e "$rline"
|
|
306
|
+
done
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#--- Diff Display (colored) ----------------------------------------------------
|
|
310
|
+
|
|
311
|
+
diff_colored() {
|
|
312
|
+
local file1="$1"
|
|
313
|
+
local file2="$2"
|
|
314
|
+
|
|
315
|
+
if command -v delta &>/dev/null; then
|
|
316
|
+
delta "$file1" "$file2"
|
|
317
|
+
elif command -v diff &>/dev/null; then
|
|
318
|
+
diff -u "$file1" "$file2" | while IFS= read -r line; do
|
|
319
|
+
case "$line" in
|
|
320
|
+
---*) echo -e "${TUI_BOLD}${line}${TUI_NC}" ;;
|
|
321
|
+
+++*) echo -e "${TUI_BOLD}${line}${TUI_NC}" ;;
|
|
322
|
+
@@*) echo -e "${TUI_CYAN}${line}${TUI_NC}" ;;
|
|
323
|
+
+*) echo -e "${TUI_GREEN}${line}${TUI_NC}" ;;
|
|
324
|
+
-*) echo -e "${TUI_RED}${line}${TUI_NC}" ;;
|
|
325
|
+
*) echo "$line" ;;
|
|
326
|
+
esac
|
|
327
|
+
done
|
|
328
|
+
fi
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# Inline diff from string
|
|
332
|
+
diff_inline() {
|
|
333
|
+
local label="$1"
|
|
334
|
+
local old_val="$2"
|
|
335
|
+
local new_val="$3"
|
|
336
|
+
echo -e " ${label}: ${TUI_RED}${old_val}${TUI_NC} ${ARROW_R} ${TUI_GREEN}${new_val}${TUI_NC}"
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#--- Status Line ---------------------------------------------------------------
|
|
340
|
+
|
|
341
|
+
status_line() {
|
|
342
|
+
local phase="$1"
|
|
343
|
+
local iteration="$2"
|
|
344
|
+
local model="$3"
|
|
345
|
+
local context_pct="${4:-0}"
|
|
346
|
+
|
|
347
|
+
# Model color
|
|
348
|
+
local model_color="$TUI_NC"
|
|
349
|
+
case "$model" in
|
|
350
|
+
*opus*) model_color="$TUI_OPUS" ;;
|
|
351
|
+
*sonnet*) model_color="$TUI_SONNET" ;;
|
|
352
|
+
*haiku*) model_color="$TUI_HAIKU" ;;
|
|
353
|
+
esac
|
|
354
|
+
|
|
355
|
+
# Context color
|
|
356
|
+
local ctx_color="$TUI_GREEN"
|
|
357
|
+
if [ "$context_pct" -ge 80 ]; then ctx_color="$TUI_RED"
|
|
358
|
+
elif [ "$context_pct" -ge 50 ]; then ctx_color="$TUI_YELLOW"
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
printf "${TUI_DIM}[${TUI_NC}${TUI_BOLD}%s${TUI_NC}${TUI_DIM}]${TUI_NC} " "$phase"
|
|
362
|
+
printf "iter:%s " "$iteration"
|
|
363
|
+
printf "${model_color}%s${TUI_NC} " "$model"
|
|
364
|
+
printf "${ctx_color}ctx:%d%%${TUI_NC}" "$context_pct"
|
|
365
|
+
echo ""
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#--- Section Headers -----------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
section_header() {
|
|
371
|
+
local title="$1"
|
|
372
|
+
local width=${2:-$TUI_COLS}
|
|
373
|
+
[ "$width" -gt 80 ] && width=80
|
|
374
|
+
|
|
375
|
+
echo ""
|
|
376
|
+
echo -e "${TUI_BOLD}${title}${TUI_NC}"
|
|
377
|
+
local line=""
|
|
378
|
+
for ((i=0; i<${#title}; i++)); do line+="${BOX_H}"; done
|
|
379
|
+
echo -e "${TUI_DIM}${line}${TUI_NC}"
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
#--- Token Cost Display --------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
display_token_cost() {
|
|
385
|
+
local model="$1"
|
|
386
|
+
local input_tokens="$2"
|
|
387
|
+
local output_tokens="$3"
|
|
388
|
+
|
|
389
|
+
# Pricing per 1M tokens (March 2026 rates)
|
|
390
|
+
local input_rate=0
|
|
391
|
+
local output_rate=0
|
|
392
|
+
case "$model" in
|
|
393
|
+
*opus*) input_rate=15.0; output_rate=75.0 ;;
|
|
394
|
+
*sonnet*) input_rate=3.0; output_rate=15.0 ;;
|
|
395
|
+
*haiku*) input_rate=0.25; output_rate=1.25 ;;
|
|
396
|
+
*gpt-4o*) input_rate=2.5; output_rate=10.0 ;;
|
|
397
|
+
*gpt-4o-mini*) input_rate=0.15; output_rate=0.6 ;;
|
|
398
|
+
*) input_rate=3.0; output_rate=15.0 ;; # default sonnet-like
|
|
399
|
+
esac
|
|
400
|
+
|
|
401
|
+
local input_cost
|
|
402
|
+
local output_cost
|
|
403
|
+
input_cost=$(echo "scale=4; $input_tokens * $input_rate / 1000000" | bc 2>/dev/null || echo "0")
|
|
404
|
+
output_cost=$(echo "scale=4; $output_tokens * $output_rate / 1000000" | bc 2>/dev/null || echo "0")
|
|
405
|
+
local total_cost
|
|
406
|
+
total_cost=$(echo "scale=4; $input_cost + $output_cost" | bc 2>/dev/null || echo "0")
|
|
407
|
+
|
|
408
|
+
local in_fmt out_fmt
|
|
409
|
+
in_fmt=$(format_tokens "$input_tokens")
|
|
410
|
+
out_fmt=$(format_tokens "$output_tokens")
|
|
411
|
+
|
|
412
|
+
echo -e " ${TUI_DIM}Input:${TUI_NC} ${in_fmt} tokens (\$${input_cost})"
|
|
413
|
+
echo -e " ${TUI_DIM}Output:${TUI_NC} ${out_fmt} tokens (\$${output_cost})"
|
|
414
|
+
echo -e " ${TUI_DIM}Total:${TUI_NC} \$${total_cost}"
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
#--- Tree Display --------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
# Display a directory tree (simple, no external deps)
|
|
420
|
+
tree_display() {
|
|
421
|
+
local dir="${1:-.}"
|
|
422
|
+
local prefix="${2:-}"
|
|
423
|
+
local depth="${3:-3}"
|
|
424
|
+
|
|
425
|
+
if [ "$depth" -le 0 ]; then
|
|
426
|
+
echo "${prefix}..."
|
|
427
|
+
return
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
local items=()
|
|
431
|
+
while IFS= read -r item; do
|
|
432
|
+
[ -n "$item" ] && items+=("$item")
|
|
433
|
+
done < <(ls -1 "$dir" 2>/dev/null | grep -v '^\.' | head -20)
|
|
434
|
+
|
|
435
|
+
local count=${#items[@]}
|
|
436
|
+
local i=0
|
|
437
|
+
for item in "${items[@]}"; do
|
|
438
|
+
i=$((i + 1))
|
|
439
|
+
local connector="${BOX_ML}${BOX_H}${BOX_H}"
|
|
440
|
+
local next_prefix="${prefix}${BOX_V} "
|
|
441
|
+
if [ "$i" -eq "$count" ]; then
|
|
442
|
+
connector="${BOX_BL}${BOX_H}${BOX_H}"
|
|
443
|
+
next_prefix="${prefix} "
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
if [ -d "$dir/$item" ]; then
|
|
447
|
+
echo -e "${prefix}${connector} ${TUI_BOLD}${item}/${TUI_NC}"
|
|
448
|
+
tree_display "$dir/$item" "$next_prefix" $((depth - 1))
|
|
449
|
+
else
|
|
450
|
+
echo -e "${prefix}${connector} ${item}"
|
|
451
|
+
fi
|
|
452
|
+
done
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
#--- Notification Banner -------------------------------------------------------
|
|
456
|
+
|
|
457
|
+
banner_info() {
|
|
458
|
+
echo -e "${TUI_BLUE}${BOX_H}${BOX_H}${BOX_H} ${TUI_BOLD}$*${TUI_NC}"
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
banner_warn() {
|
|
462
|
+
echo -e "${TUI_YELLOW}${BOX_H}${BOX_H}${BOX_H} ${TUI_BOLD}$*${TUI_NC}"
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
banner_error() {
|
|
466
|
+
echo -e "${TUI_RED}${BOX_H}${BOX_H}${BOX_H} ${TUI_BOLD}$*${TUI_NC}"
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
banner_success() {
|
|
470
|
+
echo -e "${TUI_GREEN}${BOX_H}${BOX_H}${BOX_H} ${TUI_BOLD}$*${TUI_NC}"
|
|
471
|
+
}
|
package/completions/_loki
CHANGED
|
@@ -91,6 +91,12 @@ function _loki {
|
|
|
91
91
|
completions)
|
|
92
92
|
_arguments '1:shell:(bash zsh)'
|
|
93
93
|
;;
|
|
94
|
+
context|ctx)
|
|
95
|
+
_loki_context
|
|
96
|
+
;;
|
|
97
|
+
code)
|
|
98
|
+
_loki_code
|
|
99
|
+
;;
|
|
94
100
|
esac
|
|
95
101
|
;;
|
|
96
102
|
esac
|
|
@@ -130,6 +136,8 @@ function _loki_commands {
|
|
|
130
136
|
'onboard:Analyze repo and generate CLAUDE.md'
|
|
131
137
|
'metrics:Session productivity report'
|
|
132
138
|
'share:Share session report as GitHub Gist'
|
|
139
|
+
'context:Context window management'
|
|
140
|
+
'code:Codebase intelligence'
|
|
133
141
|
'version:Show version'
|
|
134
142
|
'completions:Output shell completions'
|
|
135
143
|
'help:Show help'
|
|
@@ -343,4 +351,53 @@ function _loki_issue {
|
|
|
343
351
|
'1:subcommand:(parse view)'
|
|
344
352
|
}
|
|
345
353
|
|
|
354
|
+
function _loki_context {
|
|
355
|
+
local -a cmds
|
|
356
|
+
cmds=(
|
|
357
|
+
'show:Show context window usage breakdown'
|
|
358
|
+
'files:List files in context with token estimates'
|
|
359
|
+
'tools:Show tool token usage per origin'
|
|
360
|
+
'add:Add file to context'
|
|
361
|
+
'clear:Clear accumulated context'
|
|
362
|
+
'help:Context help'
|
|
363
|
+
)
|
|
364
|
+
_describe -t cmds 'context subcommand' cmds
|
|
365
|
+
|
|
366
|
+
case $line[1] in
|
|
367
|
+
add)
|
|
368
|
+
_files
|
|
369
|
+
;;
|
|
370
|
+
esac
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function _loki_code {
|
|
374
|
+
local -a cmds
|
|
375
|
+
cmds=(
|
|
376
|
+
'overview:Generate workspace overview'
|
|
377
|
+
'symbols:Search for symbols'
|
|
378
|
+
'deps:Show dependency graph'
|
|
379
|
+
'hotspots:Find code hotspots'
|
|
380
|
+
'diff:Show colored diff'
|
|
381
|
+
'help:Code help'
|
|
382
|
+
)
|
|
383
|
+
_describe -t cmds 'code subcommand' cmds
|
|
384
|
+
|
|
385
|
+
case $line[1] in
|
|
386
|
+
overview)
|
|
387
|
+
_arguments \
|
|
388
|
+
'--silent[Compact output]' \
|
|
389
|
+
'*:directory:_directories'
|
|
390
|
+
;;
|
|
391
|
+
deps)
|
|
392
|
+
_files
|
|
393
|
+
;;
|
|
394
|
+
hotspots)
|
|
395
|
+
_arguments \
|
|
396
|
+
'--top[Number of results]:number:'
|
|
397
|
+
;;
|
|
398
|
+
symbols)
|
|
399
|
+
;;
|
|
400
|
+
esac
|
|
401
|
+
}
|
|
402
|
+
|
|
346
403
|
_loki "$@"
|
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 secrets doctor watchdog audit metrics syslog onboard share explain plan report test ci watch telemetry agent version completions help"
|
|
8
|
+
local main_commands="start quick demo init stop pause resume status dashboard logs serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share explain plan report test ci watch telemetry agent context code run export review optimize heal migrate cluster worktree trigger failover remote version completions help"
|
|
9
9
|
|
|
10
10
|
# 1. If we are on the first argument (subcommand)
|
|
11
11
|
if [[ $cword -eq 1 ]]; then
|
|
@@ -181,6 +181,44 @@ _loki_completion() {
|
|
|
181
181
|
completions)
|
|
182
182
|
COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
|
|
183
183
|
;;
|
|
184
|
+
|
|
185
|
+
context|ctx)
|
|
186
|
+
if [[ $cword -eq 2 ]]; then
|
|
187
|
+
COMPREPLY=( $(compgen -W "show files tools add clear help" -- "$cur") )
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
if [[ "${words[2]}" == "add" ]]; then
|
|
191
|
+
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
192
|
+
return 0
|
|
193
|
+
fi
|
|
194
|
+
;;
|
|
195
|
+
|
|
196
|
+
code)
|
|
197
|
+
if [[ $cword -eq 2 ]]; then
|
|
198
|
+
COMPREPLY=( $(compgen -W "overview symbols deps hotspots diff help" -- "$cur") )
|
|
199
|
+
return 0
|
|
200
|
+
fi
|
|
201
|
+
case "${words[2]}" in
|
|
202
|
+
overview)
|
|
203
|
+
if [[ "$cur" == -* ]]; then
|
|
204
|
+
COMPREPLY=( $(compgen -W "--silent" -- "$cur") )
|
|
205
|
+
return 0
|
|
206
|
+
fi
|
|
207
|
+
_filedir -d
|
|
208
|
+
;;
|
|
209
|
+
deps)
|
|
210
|
+
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
211
|
+
;;
|
|
212
|
+
hotspots)
|
|
213
|
+
if [[ "$cur" == -* ]]; then
|
|
214
|
+
COMPREPLY=( $(compgen -W "--top" -- "$cur") )
|
|
215
|
+
return 0
|
|
216
|
+
fi
|
|
217
|
+
;;
|
|
218
|
+
symbols)
|
|
219
|
+
;;
|
|
220
|
+
esac
|
|
221
|
+
;;
|
|
184
222
|
esac
|
|
185
223
|
}
|
|
186
224
|
|