@tekmidian/pai 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +73 -8
- package/dist/cli/index.mjs +100 -3
- package/dist/cli/index.mjs.map +1 -1
- package/dist/daemon-mcp/index.mjs +37 -6
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/skills/Art/SKILL.md +32 -0
- package/dist/skills/Createskill/SKILL.md +23 -0
- package/dist/skills/Journal/SKILL.md +34 -0
- package/dist/skills/Name/SKILL.md +12 -0
- package/dist/skills/Observability/SKILL.md +25 -0
- package/dist/skills/Plan/SKILL.md +28 -0
- package/dist/skills/Research/SKILL.md +30 -0
- package/dist/skills/Review/SKILL.md +58 -0
- package/dist/skills/Route/SKILL.md +14 -0
- package/dist/skills/SearchHistory/SKILL.md +31 -0
- package/dist/skills/Sessions/SKILL.md +28 -0
- package/dist/skills/Share/SKILL.md +35 -0
- package/dist/skills/StoryExplanation/SKILL.md +21 -0
- package/dist/skills/VaultConnect/SKILL.md +10 -0
- package/dist/skills/VaultContext/SKILL.md +12 -0
- package/dist/skills/VaultEmerge/SKILL.md +10 -0
- package/dist/skills/VaultOrphans/SKILL.md +10 -0
- package/dist/skills/VaultTrace/SKILL.md +10 -0
- package/package.json +3 -2
- package/scripts/build-skill-stubs.mjs +202 -0
- package/statusline-command.sh +121 -9
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate SKILL.md files from MCP prompt definitions.
|
|
4
|
+
*
|
|
5
|
+
* Sources (in order):
|
|
6
|
+
* 1. src/daemon-mcp/prompts/*.ts — PAI built-in prompts (tracked in git)
|
|
7
|
+
* 2. src/daemon-mcp/prompts/custom/*.ts — User-created prompts (gitignored)
|
|
8
|
+
*
|
|
9
|
+
* Each prompt becomes a discoverable Claude Code skill at:
|
|
10
|
+
* dist/skills/<TitleCase>/SKILL.md
|
|
11
|
+
*
|
|
12
|
+
* Installation: `pai setup` or `pai skills sync` symlinks (macOS/Linux)
|
|
13
|
+
* or copies (Windows) each skill directory into ~/.claude/skills/.
|
|
14
|
+
*
|
|
15
|
+
* With --sync: also creates/updates symlinks in ~/.claude/skills/ after
|
|
16
|
+
* generating stubs. This is called automatically during `bun run build`.
|
|
17
|
+
*
|
|
18
|
+
* Source of truth: the TypeScript prompt files. Skills are regenerated on
|
|
19
|
+
* every build — never edit the generated SKILL.md files by hand.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
readFileSync,
|
|
24
|
+
writeFileSync,
|
|
25
|
+
mkdirSync,
|
|
26
|
+
existsSync,
|
|
27
|
+
readdirSync,
|
|
28
|
+
symlinkSync,
|
|
29
|
+
lstatSync,
|
|
30
|
+
readlinkSync,
|
|
31
|
+
unlinkSync,
|
|
32
|
+
} from "fs";
|
|
33
|
+
import { join, resolve } from "path";
|
|
34
|
+
import { homedir, platform } from "os";
|
|
35
|
+
|
|
36
|
+
const PROMPTS_DIR = "src/daemon-mcp/prompts";
|
|
37
|
+
const CUSTOM_DIR = join(PROMPTS_DIR, "custom");
|
|
38
|
+
const STUBS_OUT = "dist/skills";
|
|
39
|
+
const doSync = process.argv.includes("--sync");
|
|
40
|
+
|
|
41
|
+
// kebab-case → TitleCase for Claude Code's skill scanner
|
|
42
|
+
function toTitleCase(promptName) {
|
|
43
|
+
return promptName
|
|
44
|
+
.split("-")
|
|
45
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
46
|
+
.join("");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Extract description and full content from a prompt .ts file
|
|
50
|
+
function parsePrompt(filePath) {
|
|
51
|
+
const src = readFileSync(filePath, "utf-8");
|
|
52
|
+
|
|
53
|
+
// Extract the description field
|
|
54
|
+
const descMatch = src.match(/description:\s*["'`]([^"'`]+)["'`]/);
|
|
55
|
+
if (!descMatch) return null;
|
|
56
|
+
const description = descMatch[1];
|
|
57
|
+
|
|
58
|
+
// Extract USE WHEN line
|
|
59
|
+
const useWhenMatch = src.match(/USE WHEN[^\n]+/);
|
|
60
|
+
const useWhen = useWhenMatch ? useWhenMatch[0] : "";
|
|
61
|
+
|
|
62
|
+
// Extract the full content template string
|
|
63
|
+
// Match content: `...` handling escaped backticks (\`) inside
|
|
64
|
+
const contentMatch = src.match(/content:\s*`((?:[^`\\]|\\[\s\S])*)`/);
|
|
65
|
+
if (!contentMatch) return null;
|
|
66
|
+
// Unescape template literal escapes
|
|
67
|
+
const content = contentMatch[1]
|
|
68
|
+
.replace(/\\`/g, "`")
|
|
69
|
+
.replace(/\\\$/g, "$")
|
|
70
|
+
.replace(/\\'/g, "'")
|
|
71
|
+
.replace(/\\n/g, "\n");
|
|
72
|
+
|
|
73
|
+
return { description, useWhen, content };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Read export list from index.ts to get built-in prompt file names
|
|
77
|
+
function getBuiltinPromptNames() {
|
|
78
|
+
const indexSrc = readFileSync(join(PROMPTS_DIR, "index.ts"), "utf-8");
|
|
79
|
+
const names = [];
|
|
80
|
+
for (const match of indexSrc.matchAll(
|
|
81
|
+
/export\s+\{[^}]+\}\s+from\s+["']\.\/([^"']+)\.js["']/g
|
|
82
|
+
)) {
|
|
83
|
+
names.push(match[1]);
|
|
84
|
+
}
|
|
85
|
+
return names;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Scan custom/ directory for user-created prompt files
|
|
89
|
+
function getCustomPromptNames() {
|
|
90
|
+
if (!existsSync(CUSTOM_DIR)) return [];
|
|
91
|
+
return readdirSync(CUSTOM_DIR)
|
|
92
|
+
.filter((f) => f.endsWith(".ts") && f !== "index.ts")
|
|
93
|
+
.map((f) => f.replace(/\.ts$/, ""));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Sync symlinks: create/update symlinks in ~/.claude/skills/
|
|
97
|
+
function syncSymlinks(generatedNames) {
|
|
98
|
+
const skillsDir = join(homedir(), ".claude", "skills");
|
|
99
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
100
|
+
|
|
101
|
+
const useSymlinks = platform() !== "win32";
|
|
102
|
+
let created = 0;
|
|
103
|
+
let updated = 0;
|
|
104
|
+
let current = 0;
|
|
105
|
+
|
|
106
|
+
for (const name of generatedNames) {
|
|
107
|
+
const source = resolve(join(STUBS_OUT, name));
|
|
108
|
+
const target = join(skillsDir, name);
|
|
109
|
+
|
|
110
|
+
// Never overwrite non-symlink directories (user's own skills)
|
|
111
|
+
if (existsSync(target) && !lstatSync(target).isSymbolicLink()) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check existing symlink
|
|
116
|
+
if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
|
|
117
|
+
if (resolve(readlinkSync(target)) === source) {
|
|
118
|
+
current++;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
unlinkSync(target);
|
|
122
|
+
updated++;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (useSymlinks) {
|
|
126
|
+
symlinkSync(source, target);
|
|
127
|
+
} else {
|
|
128
|
+
mkdirSync(target, { recursive: true });
|
|
129
|
+
writeFileSync(
|
|
130
|
+
join(target, "SKILL.md"),
|
|
131
|
+
readFileSync(join(source, "SKILL.md")),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
created++;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Clean up stale symlinks from old user/ location
|
|
138
|
+
const oldUserDir = join(skillsDir, "user");
|
|
139
|
+
if (existsSync(oldUserDir)) {
|
|
140
|
+
for (const name of generatedNames) {
|
|
141
|
+
const oldTarget = join(oldUserDir, name);
|
|
142
|
+
if (existsSync(oldTarget) && lstatSync(oldTarget).isSymbolicLink()) {
|
|
143
|
+
unlinkSync(oldTarget);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const parts = [];
|
|
149
|
+
if (created > 0) parts.push(`${created} created`);
|
|
150
|
+
if (updated > 0) parts.push(`${updated} updated`);
|
|
151
|
+
if (current > 0) parts.push(`${current} current`);
|
|
152
|
+
console.log(`✔ Skill symlinks synced: ${parts.join(", ")}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// --- Main ---
|
|
156
|
+
|
|
157
|
+
const builtinNames = getBuiltinPromptNames();
|
|
158
|
+
const customNames = getCustomPromptNames();
|
|
159
|
+
let generated = 0;
|
|
160
|
+
const generatedDirNames = [];
|
|
161
|
+
|
|
162
|
+
mkdirSync(STUBS_OUT, { recursive: true });
|
|
163
|
+
|
|
164
|
+
// Process all prompts (built-in + custom)
|
|
165
|
+
for (const [fileName, dir] of [
|
|
166
|
+
...builtinNames.map((n) => [n, PROMPTS_DIR]),
|
|
167
|
+
...customNames.map((n) => [n, CUSTOM_DIR]),
|
|
168
|
+
]) {
|
|
169
|
+
const filePath = join(dir, `${fileName}.ts`);
|
|
170
|
+
const parsed = parsePrompt(filePath);
|
|
171
|
+
if (!parsed) {
|
|
172
|
+
console.warn(`⚠ Skipping ${fileName}: could not parse`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const dirName = toTitleCase(fileName);
|
|
177
|
+
const outDir = join(STUBS_OUT, dirName);
|
|
178
|
+
const outFile = join(outDir, "SKILL.md");
|
|
179
|
+
|
|
180
|
+
mkdirSync(outDir, { recursive: true });
|
|
181
|
+
|
|
182
|
+
const skill = [
|
|
183
|
+
"---",
|
|
184
|
+
`name: ${dirName}`,
|
|
185
|
+
`description: "${parsed.description}. ${parsed.useWhen}"`,
|
|
186
|
+
"---",
|
|
187
|
+
"",
|
|
188
|
+
parsed.content.trim(),
|
|
189
|
+
"",
|
|
190
|
+
].join("\n");
|
|
191
|
+
|
|
192
|
+
writeFileSync(outFile, skill);
|
|
193
|
+
generatedDirNames.push(dirName);
|
|
194
|
+
generated++;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const customLabel = customNames.length > 0 ? ` (${builtinNames.length} built-in + ${customNames.length} custom)` : "";
|
|
198
|
+
console.log(`✔ ${generated} skill stubs generated in ${STUBS_OUT}/${customLabel}`);
|
|
199
|
+
|
|
200
|
+
if (doSync) {
|
|
201
|
+
syncSymlinks(generatedDirNames);
|
|
202
|
+
}
|
package/statusline-command.sh
CHANGED
|
@@ -310,8 +310,8 @@ fi
|
|
|
310
310
|
|
|
311
311
|
# Output the statusline
|
|
312
312
|
# LINE 1 - Greeting (adaptive: drop CC version when narrow, shorten further if very narrow)
|
|
313
|
-
line1_full="${EMOJI_WAVE} ${DA_DISPLAY_COLOR}
|
|
314
|
-
line1_medium="${EMOJI_WAVE} ${DA_DISPLAY_COLOR}
|
|
313
|
+
line1_full="${EMOJI_WAVE} ${DA_DISPLAY_COLOR}${DA_NAME}${RESET} ${MODEL_PURPLE}CC ${cc_version}${RESET}${LINE1_PRIMARY} ${MODEL_PURPLE}${EMOJI_BRAIN} ${model_name}${RESET}${LINE1_PRIMARY} in ${DIR_COLOR}${EMOJI_FOLDER} ${dir_name}${BRIGHT_CYAN}${session_suffix}${RESET}"
|
|
314
|
+
line1_medium="${EMOJI_WAVE} ${DA_DISPLAY_COLOR}${DA_NAME}${RESET} ${MODEL_PURPLE}${EMOJI_BRAIN} ${model_name}${RESET}${LINE1_PRIMARY} in ${DIR_COLOR}${EMOJI_FOLDER} ${dir_name}${BRIGHT_CYAN}${session_suffix}${RESET}"
|
|
315
315
|
line1_short="${EMOJI_WAVE} ${MODEL_PURPLE}${EMOJI_BRAIN} ${model_name}${RESET}${LINE1_PRIMARY} ${DIR_COLOR}${EMOJI_FOLDER} ${dir_name}${BRIGHT_CYAN}${session_suffix}${RESET}"
|
|
316
316
|
|
|
317
317
|
# Pick line 1 format based on width (plain-text lengths: full~85, medium~45, short~25)
|
|
@@ -330,13 +330,125 @@ if [ -n "$mcp_line2" ]; then
|
|
|
330
330
|
printf "${LINE2_PRIMARY} ${RESET}${mcp_line2}${RESET}\n"
|
|
331
331
|
fi
|
|
332
332
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
333
|
+
|
|
334
|
+
# Fetch OAuth usage (5-hour current + 7-day weekly) with caching
|
|
335
|
+
usage_cache="/tmp/claude/statusline-usage-cache.json"
|
|
336
|
+
usage_cache_ttl=60 # seconds
|
|
337
|
+
usage_suffix=""
|
|
338
|
+
|
|
339
|
+
_fetch_usage() {
|
|
340
|
+
# Try to get OAuth token from macOS Keychain
|
|
341
|
+
local token=""
|
|
342
|
+
token=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
|
|
343
|
+
[ -z "$token" ] && return
|
|
344
|
+
|
|
345
|
+
mkdir -p /tmp/claude
|
|
346
|
+
local response
|
|
347
|
+
response=$(curl -sf --max-time 3 \
|
|
348
|
+
-H "Authorization: Bearer $token" \
|
|
349
|
+
-H "anthropic-beta: oauth-2025-04-20" \
|
|
350
|
+
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
|
|
351
|
+
[ -n "$response" ] && echo "$response" > "$usage_cache"
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# Use cache if fresh, otherwise fetch in background
|
|
355
|
+
if [ -f "$usage_cache" ]; then
|
|
356
|
+
cache_age=$(( $(date +%s) - $(stat -f %m "$usage_cache" 2>/dev/null || echo 0) ))
|
|
357
|
+
if [ "$cache_age" -gt "$usage_cache_ttl" ]; then
|
|
358
|
+
_fetch_usage &
|
|
359
|
+
fi
|
|
360
|
+
else
|
|
361
|
+
_fetch_usage &
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
# Read cached usage data
|
|
365
|
+
if [ -f "$usage_cache" ]; then
|
|
366
|
+
five_hour=$(jq -r '.five_hour.utilization // 0' "$usage_cache" 2>/dev/null)
|
|
367
|
+
seven_day=$(jq -r '.seven_day.utilization // 0' "$usage_cache" 2>/dev/null)
|
|
368
|
+
five_reset=$(jq -r '.five_hour.resets_at // empty' "$usage_cache" 2>/dev/null)
|
|
369
|
+
seven_reset=$(jq -r '.seven_day.resets_at // empty' "$usage_cache" 2>/dev/null)
|
|
370
|
+
|
|
371
|
+
# Round to integers
|
|
372
|
+
five_hour_int=$(printf "%.0f" "$five_hour" 2>/dev/null || echo 0)
|
|
373
|
+
seven_day_int=$(printf "%.0f" "$seven_day" 2>/dev/null || echo 0)
|
|
374
|
+
|
|
375
|
+
# Format reset times as HH:MM (local time)
|
|
376
|
+
five_reset_fmt=""
|
|
377
|
+
seven_reset_fmt=""
|
|
378
|
+
seven_reset_epoch=0
|
|
379
|
+
if [ -n "$five_reset" ]; then
|
|
380
|
+
five_reset_fmt=$(date -jf "%Y-%m-%dT%H:%M:%S" "$(echo "$five_reset" | cut -c1-19)" "+%H:%M" 2>/dev/null || date -d "$five_reset" "+%H:%M" 2>/dev/null || echo "")
|
|
381
|
+
fi
|
|
382
|
+
if [ -n "$seven_reset" ]; then
|
|
383
|
+
seven_reset_fmt=$(date -jf "%Y-%m-%dT%H:%M:%S" "$(echo "$seven_reset" | cut -c1-19)" "+%a %H:%M" 2>/dev/null || date -d "$seven_reset" "+%a %H:%M" 2>/dev/null || echo "")
|
|
384
|
+
seven_reset_epoch=$(date -jf "%Y-%m-%dT%H:%M:%S" "$(echo "$seven_reset" | cut -c1-19)" "+%s" 2>/dev/null || date -d "$seven_reset" "+%s" 2>/dev/null || echo 0)
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
# Color based on utilization: green < 50%, yellow 50-75%, red > 75%
|
|
388
|
+
_usage_color() {
|
|
389
|
+
local pct=$1
|
|
390
|
+
if [ "$pct" -gt 75 ] 2>/dev/null; then echo "$BRIGHT_RED"
|
|
391
|
+
elif [ "$pct" -gt 50 ] 2>/dev/null; then echo "$BRIGHT_YELLOW"
|
|
392
|
+
else echo "$BRIGHT_GREEN"; fi
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
five_color=$(_usage_color "$five_hour_int")
|
|
396
|
+
seven_color=$(_usage_color "$seven_day_int")
|
|
397
|
+
|
|
398
|
+
# Budget pace indicator for 7-day window
|
|
399
|
+
# Compare actual usage vs linear expected usage based on elapsed time
|
|
400
|
+
pace_dot=""
|
|
401
|
+
if [ "$seven_reset_epoch" -gt 0 ] 2>/dev/null; then
|
|
402
|
+
now_epoch=$(date +%s)
|
|
403
|
+
window_secs=$((7 * 86400))
|
|
404
|
+
remaining_secs=$((seven_reset_epoch - now_epoch))
|
|
405
|
+
[ "$remaining_secs" -lt 0 ] && remaining_secs=0
|
|
406
|
+
elapsed_secs=$((window_secs - remaining_secs))
|
|
407
|
+
# Expected usage if spending linearly: elapsed/total * 100
|
|
408
|
+
expected_pct=$(( elapsed_secs * 100 / window_secs ))
|
|
409
|
+
# Daily pace: actual spend per day vs budget (100/7 ≈ 14%/day)
|
|
410
|
+
elapsed_days_x10=$((elapsed_secs * 10 / 86400))
|
|
411
|
+
[ "$elapsed_days_x10" -lt 1 ] && elapsed_days_x10=1
|
|
412
|
+
spend_per_day=$((seven_day_int * 10 / elapsed_days_x10))
|
|
413
|
+
budget_per_day=14 # 100/7 ≈ 14%
|
|
414
|
+
# Color: green = under budget, orange = near budget, red = over budget
|
|
415
|
+
overspend=$((spend_per_day - budget_per_day))
|
|
416
|
+
if [ "$overspend" -le -3 ] 2>/dev/null; then
|
|
417
|
+
pace_color="$BRIGHT_GREEN" # well under budget
|
|
418
|
+
elif [ "$overspend" -le 2 ] 2>/dev/null; then
|
|
419
|
+
pace_color="$BRIGHT_ORANGE" # near budget
|
|
420
|
+
else
|
|
421
|
+
pace_color="$BRIGHT_RED" # over budget
|
|
422
|
+
fi
|
|
423
|
+
pace_dot="${pace_color}${spend_per_day}%% / ${budget_per_day}%%${RESET}"
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
# Build usage suffix: 5h: 8% → 00:59 │ 1d: ● 29% / 36% │ 7d: 29% → Fr. 08:00
|
|
427
|
+
five_label="5h: ${five_hour_int}%%"
|
|
428
|
+
[ -n "$five_reset_fmt" ] && five_label="${five_label} → ${five_reset_fmt}"
|
|
429
|
+
seven_label="7d: ${seven_day_int}%%"
|
|
430
|
+
[ -n "$seven_reset_fmt" ] && seven_label="${seven_label} → ${seven_reset_fmt}"
|
|
431
|
+
|
|
432
|
+
usage_suffix=" ${SEPARATOR_COLOR}│${RESET} ${five_color}${five_label}${RESET}"
|
|
433
|
+
[ -n "$pace_dot" ] && usage_suffix="${usage_suffix} ${SEPARATOR_COLOR}│${RESET} ${LINE3_PRIMARY}1d:${RESET} ${pace_dot}"
|
|
434
|
+
usage_suffix="${usage_suffix} ${SEPARATOR_COLOR}│${RESET} ${seven_color}${seven_label}${RESET}"
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
# LINE 3 - Context meter + usage limits
|
|
438
|
+
# Auto-compact remaining: how much context left until compaction triggers
|
|
439
|
+
ac_threshold="${CLAUDE_AUTOCOMPACT_PCT_OVERRIDE:-80}"
|
|
440
|
+
ac_remaining=$((ac_threshold - context_pct))
|
|
441
|
+
[ "$ac_remaining" -lt 0 ] && ac_remaining=0
|
|
442
|
+
# Color the remaining %: red ≤5, yellow ≤15, green otherwise
|
|
443
|
+
if [ "$ac_remaining" -le 5 ] 2>/dev/null; then
|
|
444
|
+
ac_color="$BRIGHT_RED"
|
|
445
|
+
elif [ "$ac_remaining" -le 15 ] 2>/dev/null; then
|
|
446
|
+
ac_color="$BRIGHT_YELLOW"
|
|
447
|
+
else
|
|
448
|
+
ac_color="$BRIGHT_GREEN"
|
|
337
449
|
fi
|
|
450
|
+
ac_suffix=" ${ac_color}(${ac_remaining}%%)${RESET}"
|
|
338
451
|
|
|
339
|
-
# LINE 3 - Context meter (from Claude Code's JSON input)
|
|
340
452
|
if [ "$context_pct" -gt 0 ] 2>/dev/null; then
|
|
341
453
|
# Color based on usage: green < 50%, yellow 50-75%, red > 75%
|
|
342
454
|
if [ $context_pct -gt 75 ]; then
|
|
@@ -347,7 +459,7 @@ if [ "$context_pct" -gt 0 ] 2>/dev/null; then
|
|
|
347
459
|
ctx_color="$BRIGHT_GREEN"
|
|
348
460
|
fi
|
|
349
461
|
|
|
350
|
-
printf "${LINE3_PRIMARY}${EMOJI_GEM} Context${RESET}${LINE3_PRIMARY}${SEPARATOR_COLOR}: ${RESET}${ctx_color}${context_used_k}K${RESET}${LINE3_PRIMARY} / ${context_max_k}K${
|
|
462
|
+
printf "${LINE3_PRIMARY}${EMOJI_GEM} Context${RESET}${LINE3_PRIMARY}${SEPARATOR_COLOR}: ${RESET}${ctx_color}${context_used_k}K${RESET}${LINE3_PRIMARY} / ${context_max_k}K${ac_suffix}${usage_suffix}${RESET}\n"
|
|
351
463
|
else
|
|
352
|
-
printf "${LINE3_PRIMARY}${EMOJI_GEM} Context${RESET}${LINE3_PRIMARY}${SEPARATOR_COLOR}: ${RESET}${LINE3_ACCENT}...${
|
|
464
|
+
printf "${LINE3_PRIMARY}${EMOJI_GEM} Context${RESET}${LINE3_PRIMARY}${SEPARATOR_COLOR}: ${RESET}${LINE3_ACCENT}...${ac_suffix}${usage_suffix}${RESET}\n"
|
|
353
465
|
fi
|