agileflow 2.75.0 → 2.77.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/package.json +1 -1
- package/scripts/agileflow-configure.js +110 -32
- package/scripts/agileflow-statusline.sh +155 -9
- package/scripts/agileflow-welcome.js +171 -47
- package/scripts/auto-self-improve.js +344 -0
- package/scripts/check-update.js +1 -4
- package/scripts/get-env.js +15 -7
- package/scripts/lib/frontmatter-parser.js +4 -1
- package/scripts/obtain-context.js +59 -48
- package/scripts/ralph-loop.js +503 -0
- package/scripts/validate-expertise.sh +19 -15
- package/src/core/agents/design.md +1 -1
- package/src/core/agents/documentation.md +1 -1
- package/src/core/agents/integrations.md +1 -1
- package/src/core/agents/mobile.md +1 -1
- package/src/core/agents/monitoring.md +1 -1
- package/src/core/agents/performance.md +1 -1
- package/src/core/commands/babysit.md +73 -0
- package/src/core/commands/configure.md +50 -7
- package/src/core/experts/documentation/expertise.yaml +4 -0
- package/src/core/experts/research/expertise.yaml +2 -2
- package/tools/cli/commands/list.js +3 -1
- package/tools/cli/commands/uninstall.js +4 -5
- package/tools/cli/commands/update.js +11 -3
- package/tools/cli/lib/content-injector.js +6 -1
- package/tools/cli/lib/docs-setup.js +1 -1
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* --detect Show current status
|
|
24
24
|
* --help Show help
|
|
25
25
|
*
|
|
26
|
-
* Features: sessionstart, precompact, archival, statusline, autoupdate
|
|
26
|
+
* Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
const fs = require('fs');
|
|
@@ -71,7 +71,8 @@ const VERSION = getVersion();
|
|
|
71
71
|
const FEATURES = {
|
|
72
72
|
sessionstart: { hook: 'SessionStart', script: 'agileflow-welcome.js', type: 'node' },
|
|
73
73
|
precompact: { hook: 'PreCompact', script: 'precompact-context.sh', type: 'bash' },
|
|
74
|
-
|
|
74
|
+
ralphloop: { hook: 'Stop', script: 'ralph-loop.js', type: 'node' },
|
|
75
|
+
selfimprove: { hook: 'Stop', script: 'auto-self-improve.js', type: 'node' },
|
|
75
76
|
archival: { script: 'archive-completed-stories.sh', requiresHook: 'sessionstart' },
|
|
76
77
|
statusline: { script: 'agileflow-statusline.sh' },
|
|
77
78
|
autoupdate: { metadataOnly: true }, // Stored in metadata.updates.autoUpdate
|
|
@@ -82,6 +83,8 @@ const ALL_SCRIPTS = {
|
|
|
82
83
|
// Core feature scripts (linked to FEATURES)
|
|
83
84
|
'agileflow-welcome.js': { feature: 'sessionstart', required: true },
|
|
84
85
|
'precompact-context.sh': { feature: 'precompact', required: true },
|
|
86
|
+
'ralph-loop.js': { feature: 'ralphloop', required: true },
|
|
87
|
+
'auto-self-improve.js': { feature: 'selfimprove', required: true },
|
|
85
88
|
'archive-completed-stories.sh': { feature: 'archival', required: true },
|
|
86
89
|
'agileflow-statusline.sh': { feature: 'statusline', required: true },
|
|
87
90
|
|
|
@@ -119,25 +122,25 @@ const STATUSLINE_COMPONENTS = [
|
|
|
119
122
|
|
|
120
123
|
const PROFILES = {
|
|
121
124
|
full: {
|
|
122
|
-
description: 'All features enabled',
|
|
123
|
-
enable: ['sessionstart', 'precompact', 'archival', 'statusline'],
|
|
125
|
+
description: 'All features enabled (including experimental Stop hooks)',
|
|
126
|
+
enable: ['sessionstart', 'precompact', 'archival', 'statusline', 'ralphloop', 'selfimprove'],
|
|
124
127
|
archivalDays: 30,
|
|
125
128
|
},
|
|
126
129
|
basic: {
|
|
127
130
|
description: 'Essential hooks + archival (SessionStart + PreCompact + Archival)',
|
|
128
131
|
enable: ['sessionstart', 'precompact', 'archival'],
|
|
129
|
-
disable: ['statusline'],
|
|
132
|
+
disable: ['statusline', 'ralphloop', 'selfimprove'],
|
|
130
133
|
archivalDays: 30,
|
|
131
134
|
},
|
|
132
135
|
minimal: {
|
|
133
136
|
description: 'SessionStart + archival only',
|
|
134
137
|
enable: ['sessionstart', 'archival'],
|
|
135
|
-
disable: ['precompact', 'statusline'],
|
|
138
|
+
disable: ['precompact', 'statusline', 'ralphloop', 'selfimprove'],
|
|
136
139
|
archivalDays: 30,
|
|
137
140
|
},
|
|
138
141
|
none: {
|
|
139
142
|
description: 'Disable all AgileFlow features',
|
|
140
|
-
disable: ['sessionstart', 'precompact', 'archival', 'statusline'],
|
|
143
|
+
disable: ['sessionstart', 'precompact', 'archival', 'statusline', 'ralphloop', 'selfimprove'],
|
|
141
144
|
},
|
|
142
145
|
};
|
|
143
146
|
|
|
@@ -208,6 +211,8 @@ function detectConfig() {
|
|
|
208
211
|
features: {
|
|
209
212
|
sessionstart: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
210
213
|
precompact: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
214
|
+
ralphloop: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
215
|
+
selfimprove: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
211
216
|
archival: { enabled: false, threshold: null, version: null, outdated: false },
|
|
212
217
|
statusline: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
213
218
|
},
|
|
@@ -276,7 +281,23 @@ function detectConfig() {
|
|
|
276
281
|
}
|
|
277
282
|
}
|
|
278
283
|
|
|
279
|
-
//
|
|
284
|
+
// Stop hooks (ralphloop and selfimprove)
|
|
285
|
+
if (settings.hooks.Stop) {
|
|
286
|
+
if (Array.isArray(settings.hooks.Stop) && settings.hooks.Stop.length > 0) {
|
|
287
|
+
const hook = settings.hooks.Stop[0];
|
|
288
|
+
if (hook.matcher !== undefined && hook.hooks) {
|
|
289
|
+
// Check for each Stop hook feature
|
|
290
|
+
for (const h of hook.hooks) {
|
|
291
|
+
if (h.command?.includes('ralph-loop')) {
|
|
292
|
+
status.features.ralphloop.enabled = true;
|
|
293
|
+
}
|
|
294
|
+
if (h.command?.includes('auto-self-improve')) {
|
|
295
|
+
status.features.selfimprove.enabled = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
280
301
|
}
|
|
281
302
|
|
|
282
303
|
// StatusLine
|
|
@@ -370,6 +391,8 @@ function printStatus(status) {
|
|
|
370
391
|
|
|
371
392
|
printFeature('sessionstart', 'SessionStart Hook');
|
|
372
393
|
printFeature('precompact', 'PreCompact Hook');
|
|
394
|
+
printFeature('ralphloop', 'RalphLoop (Stop)');
|
|
395
|
+
printFeature('selfimprove', 'SelfImprove (Stop)');
|
|
373
396
|
|
|
374
397
|
const arch = status.features.archival;
|
|
375
398
|
log(
|
|
@@ -417,9 +440,9 @@ function migrateSettings() {
|
|
|
417
440
|
|
|
418
441
|
let migrated = false;
|
|
419
442
|
|
|
420
|
-
// Migrate hooks
|
|
443
|
+
// Migrate hooks to new format
|
|
421
444
|
if (settings.hooks) {
|
|
422
|
-
['SessionStart', 'PreCompact', 'UserPromptSubmit'].forEach(hookName => {
|
|
445
|
+
['SessionStart', 'PreCompact', 'UserPromptSubmit', 'Stop'].forEach(hookName => {
|
|
423
446
|
const hook = settings.hooks[hookName];
|
|
424
447
|
if (!hook) return;
|
|
425
448
|
|
|
@@ -556,16 +579,45 @@ function enableFeature(feature, options = {}) {
|
|
|
556
579
|
return false;
|
|
557
580
|
}
|
|
558
581
|
|
|
559
|
-
//
|
|
560
|
-
const
|
|
582
|
+
// Use absolute path so hooks work from any subdirectory
|
|
583
|
+
const absoluteScriptPath = path.join(process.cwd(), scriptPath);
|
|
584
|
+
|
|
585
|
+
// Stop hooks use error suppression to avoid blocking Claude
|
|
586
|
+
const isStoHook = config.hook === 'Stop';
|
|
587
|
+
const command =
|
|
588
|
+
config.type === 'node'
|
|
589
|
+
? `node ${absoluteScriptPath}${isStoHook ? ' 2>/dev/null || true' : ''}`
|
|
590
|
+
: `bash ${absoluteScriptPath}${isStoHook ? ' 2>/dev/null || true' : ''}`;
|
|
591
|
+
|
|
592
|
+
if (isStoHook) {
|
|
593
|
+
// Stop hooks stack - add to existing hooks instead of replacing
|
|
594
|
+
if (!settings.hooks.Stop) {
|
|
595
|
+
settings.hooks.Stop = [{ matcher: '', hooks: [] }];
|
|
596
|
+
} else if (!Array.isArray(settings.hooks.Stop) || settings.hooks.Stop.length === 0) {
|
|
597
|
+
settings.hooks.Stop = [{ matcher: '', hooks: [] }];
|
|
598
|
+
} else if (!settings.hooks.Stop[0].hooks) {
|
|
599
|
+
settings.hooks.Stop[0].hooks = [];
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Check if this script is already added
|
|
603
|
+
const hasHook = settings.hooks.Stop[0].hooks.some(h => h.command?.includes(config.script));
|
|
561
604
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
605
|
+
if (!hasHook) {
|
|
606
|
+
settings.hooks.Stop[0].hooks.push({ type: 'command', command });
|
|
607
|
+
success(`Stop hook added (${config.script})`);
|
|
608
|
+
} else {
|
|
609
|
+
info(`${feature} already enabled`);
|
|
610
|
+
}
|
|
611
|
+
} else {
|
|
612
|
+
// Other hooks (SessionStart, PreCompact) replace entirely
|
|
613
|
+
settings.hooks[config.hook] = [
|
|
614
|
+
{
|
|
615
|
+
matcher: '',
|
|
616
|
+
hooks: [{ type: 'command', command }],
|
|
617
|
+
},
|
|
618
|
+
];
|
|
619
|
+
success(`${config.hook} hook enabled (${config.script})`);
|
|
620
|
+
}
|
|
569
621
|
}
|
|
570
622
|
|
|
571
623
|
// Handle archival
|
|
@@ -579,7 +631,8 @@ function enableFeature(feature, options = {}) {
|
|
|
579
631
|
return false;
|
|
580
632
|
}
|
|
581
633
|
|
|
582
|
-
//
|
|
634
|
+
// Use absolute path so hooks work from any subdirectory
|
|
635
|
+
const absoluteScriptPath = path.join(process.cwd(), scriptPath);
|
|
583
636
|
if (settings.hooks.SessionStart?.[0]?.hooks) {
|
|
584
637
|
const hasArchival = settings.hooks.SessionStart[0].hooks.some(h =>
|
|
585
638
|
h.command?.includes('archive-completed-stories')
|
|
@@ -587,7 +640,7 @@ function enableFeature(feature, options = {}) {
|
|
|
587
640
|
if (!hasArchival) {
|
|
588
641
|
settings.hooks.SessionStart[0].hooks.push({
|
|
589
642
|
type: 'command',
|
|
590
|
-
command: `bash ${
|
|
643
|
+
command: `bash ${absoluteScriptPath} --quiet`,
|
|
591
644
|
});
|
|
592
645
|
}
|
|
593
646
|
}
|
|
@@ -607,9 +660,11 @@ function enableFeature(feature, options = {}) {
|
|
|
607
660
|
return false;
|
|
608
661
|
}
|
|
609
662
|
|
|
663
|
+
// Use absolute path so hooks work from any subdirectory
|
|
664
|
+
const absoluteScriptPath = path.join(process.cwd(), scriptPath);
|
|
610
665
|
settings.statusLine = {
|
|
611
666
|
type: 'command',
|
|
612
|
-
command: `bash ${
|
|
667
|
+
command: `bash ${absoluteScriptPath}`,
|
|
613
668
|
padding: 0,
|
|
614
669
|
};
|
|
615
670
|
success('Status line enabled');
|
|
@@ -656,8 +711,28 @@ function disableFeature(feature) {
|
|
|
656
711
|
|
|
657
712
|
// Disable hook
|
|
658
713
|
if (config.hook && settings.hooks?.[config.hook]) {
|
|
659
|
-
|
|
660
|
-
|
|
714
|
+
if (config.hook === 'Stop') {
|
|
715
|
+
// Stop hooks stack - remove only this script, not the entire hook
|
|
716
|
+
if (settings.hooks.Stop?.[0]?.hooks) {
|
|
717
|
+
const before = settings.hooks.Stop[0].hooks.length;
|
|
718
|
+
settings.hooks.Stop[0].hooks = settings.hooks.Stop[0].hooks.filter(
|
|
719
|
+
h => !h.command?.includes(config.script)
|
|
720
|
+
);
|
|
721
|
+
const after = settings.hooks.Stop[0].hooks.length;
|
|
722
|
+
|
|
723
|
+
if (before > after) {
|
|
724
|
+
success(`Stop hook removed (${config.script})`);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// If no more Stop hooks, remove the entire Stop hook
|
|
728
|
+
if (settings.hooks.Stop[0].hooks.length === 0) {
|
|
729
|
+
delete settings.hooks.Stop;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
delete settings.hooks[config.hook];
|
|
734
|
+
success(`${config.hook} hook disabled`);
|
|
735
|
+
}
|
|
661
736
|
}
|
|
662
737
|
|
|
663
738
|
// Disable archival
|
|
@@ -951,9 +1026,11 @@ function showVersionInfo() {
|
|
|
951
1026
|
const installed = installedVersion.split('.').map(Number);
|
|
952
1027
|
const latest = latestVersion.split('.').map(Number);
|
|
953
1028
|
|
|
954
|
-
if (
|
|
955
|
-
|
|
956
|
-
|
|
1029
|
+
if (
|
|
1030
|
+
latest[0] > installed[0] ||
|
|
1031
|
+
(latest[0] === installed[0] && latest[1] > installed[1]) ||
|
|
1032
|
+
(latest[0] === installed[0] && latest[1] === installed[1] && latest[2] > installed[2])
|
|
1033
|
+
) {
|
|
957
1034
|
log('\n🔄 Update available! Run: npx agileflow update', c.yellow);
|
|
958
1035
|
}
|
|
959
1036
|
}
|
|
@@ -1142,8 +1219,8 @@ ${c.cyan}Usage:${c.reset}
|
|
|
1142
1219
|
node .agileflow/scripts/agileflow-configure.js [options]
|
|
1143
1220
|
|
|
1144
1221
|
${c.cyan}Profiles:${c.reset}
|
|
1145
|
-
--profile=full All features (hooks, archival, statusline)
|
|
1146
|
-
--profile=basic SessionStart + PreCompact + archival
|
|
1222
|
+
--profile=full All features (hooks, Stop hooks, archival, statusline)
|
|
1223
|
+
--profile=basic SessionStart + PreCompact + archival (no Stop hooks)
|
|
1147
1224
|
--profile=minimal SessionStart + archival only
|
|
1148
1225
|
--profile=none Disable all AgileFlow features
|
|
1149
1226
|
|
|
@@ -1151,7 +1228,9 @@ ${c.cyan}Feature Control:${c.reset}
|
|
|
1151
1228
|
--enable=<list> Enable features (comma-separated)
|
|
1152
1229
|
--disable=<list> Disable features (comma-separated)
|
|
1153
1230
|
|
|
1154
|
-
Features: sessionstart, precompact, archival, statusline
|
|
1231
|
+
Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline
|
|
1232
|
+
|
|
1233
|
+
Stop hooks (ralphloop, selfimprove) run when Claude completes/pauses
|
|
1155
1234
|
|
|
1156
1235
|
${c.cyan}Statusline Components:${c.reset}
|
|
1157
1236
|
--show=<list> Show statusline components (comma-separated)
|
|
@@ -1273,8 +1352,7 @@ function main() {
|
|
|
1273
1352
|
else if (arg.startsWith('--repair=')) {
|
|
1274
1353
|
repair = true;
|
|
1275
1354
|
repairFeature = arg.split('=')[1].trim().toLowerCase();
|
|
1276
|
-
}
|
|
1277
|
-
else if (arg === '--version' || arg === '-v') showVersion = true;
|
|
1355
|
+
} else if (arg === '--version' || arg === '-v') showVersion = true;
|
|
1278
1356
|
else if (arg === '--list-scripts' || arg === '--scripts') listScriptsMode = true;
|
|
1279
1357
|
});
|
|
1280
1358
|
|
|
@@ -29,7 +29,7 @@ MAGENTA="\033[35m"
|
|
|
29
29
|
CYAN="\033[36m"
|
|
30
30
|
WHITE="\033[37m"
|
|
31
31
|
|
|
32
|
-
# Bright foreground colors
|
|
32
|
+
# Bright foreground colors (standard ANSI)
|
|
33
33
|
BRIGHT_RED="\033[91m"
|
|
34
34
|
BRIGHT_GREEN="\033[92m"
|
|
35
35
|
BRIGHT_YELLOW="\033[93m"
|
|
@@ -37,9 +37,74 @@ BRIGHT_BLUE="\033[94m"
|
|
|
37
37
|
BRIGHT_MAGENTA="\033[95m"
|
|
38
38
|
BRIGHT_CYAN="\033[96m"
|
|
39
39
|
|
|
40
|
+
# 256-color palette (vibrant modern colors from cc-statusline)
|
|
41
|
+
# Use these for context/session indicators for better visibility
|
|
42
|
+
CTX_GREEN="\033[38;5;158m" # Mint green - healthy context
|
|
43
|
+
CTX_YELLOW="\033[38;5;215m" # Peach - moderate usage
|
|
44
|
+
CTX_ORANGE="\033[38;5;215m" # Peach/orange - high usage
|
|
45
|
+
CTX_RED="\033[38;5;203m" # Coral red - critical
|
|
46
|
+
|
|
47
|
+
SESSION_GREEN="\033[38;5;194m" # Light green - plenty of time
|
|
48
|
+
SESSION_YELLOW="\033[38;5;228m" # Light yellow - getting low
|
|
49
|
+
SESSION_RED="\033[38;5;210m" # Light pink/red - critical
|
|
50
|
+
|
|
40
51
|
# Brand color (burnt orange #e8683a = RGB 232,104,58)
|
|
41
52
|
BRAND="\033[38;2;232;104;58m"
|
|
42
53
|
|
|
54
|
+
# ============================================================================
|
|
55
|
+
# Helper Functions
|
|
56
|
+
# ============================================================================
|
|
57
|
+
|
|
58
|
+
# Progress bar with custom characters: ▓ (filled) ░ (empty)
|
|
59
|
+
# Usage: progress_bar <percent> <width>
|
|
60
|
+
# Example: progress_bar 75 10 → "▓▓▓▓▓▓▓░░░"
|
|
61
|
+
progress_bar() {
|
|
62
|
+
local pct="${1:-0}"
|
|
63
|
+
local width="${2:-10}"
|
|
64
|
+
|
|
65
|
+
# Validate and clamp percentage
|
|
66
|
+
[[ "$pct" =~ ^[0-9]+$ ]] || pct=0
|
|
67
|
+
((pct < 0)) && pct=0
|
|
68
|
+
((pct > 100)) && pct=100
|
|
69
|
+
|
|
70
|
+
local filled=$(( pct * width / 100 ))
|
|
71
|
+
local empty=$(( width - filled ))
|
|
72
|
+
|
|
73
|
+
# Build bar with custom characters
|
|
74
|
+
local bar=""
|
|
75
|
+
for ((i=0; i<filled; i++)); do bar+="▓"; done
|
|
76
|
+
for ((i=0; i<empty; i++)); do bar+="░"; done
|
|
77
|
+
|
|
78
|
+
echo "$bar"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Convert ISO timestamp to epoch seconds (cross-platform)
|
|
82
|
+
to_epoch() {
|
|
83
|
+
local ts="$1"
|
|
84
|
+
# Try gdate first (macOS with coreutils)
|
|
85
|
+
if command -v gdate >/dev/null 2>&1; then
|
|
86
|
+
gdate -d "$ts" +%s 2>/dev/null && return
|
|
87
|
+
fi
|
|
88
|
+
# Try BSD date (macOS)
|
|
89
|
+
date -u -j -f "%Y-%m-%dT%H:%M:%S%z" "${ts/Z/+0000}" +%s 2>/dev/null && return
|
|
90
|
+
# Fallback to Python
|
|
91
|
+
python3 - "$ts" <<'PY' 2>/dev/null
|
|
92
|
+
import sys, datetime
|
|
93
|
+
s=sys.argv[1].replace('Z','+00:00')
|
|
94
|
+
print(int(datetime.datetime.fromisoformat(s).timestamp()))
|
|
95
|
+
PY
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Format epoch to HH:MM
|
|
99
|
+
fmt_time_hm() {
|
|
100
|
+
local epoch="$1"
|
|
101
|
+
if date -r 0 +%s >/dev/null 2>&1; then
|
|
102
|
+
date -r "$epoch" +"%H:%M"
|
|
103
|
+
else
|
|
104
|
+
date -d "@$epoch" +"%H:%M"
|
|
105
|
+
fi
|
|
106
|
+
}
|
|
107
|
+
|
|
43
108
|
# ============================================================================
|
|
44
109
|
# Read Component Configuration
|
|
45
110
|
# ============================================================================
|
|
@@ -50,6 +115,8 @@ SHOW_STORY=true
|
|
|
50
115
|
SHOW_EPIC=true
|
|
51
116
|
SHOW_WIP=true
|
|
52
117
|
SHOW_CONTEXT=true
|
|
118
|
+
SHOW_CONTEXT_BAR=true
|
|
119
|
+
SHOW_SESSION_TIME=true
|
|
53
120
|
SHOW_COST=true
|
|
54
121
|
SHOW_GIT=true
|
|
55
122
|
|
|
@@ -65,6 +132,8 @@ if [ -f "docs/00-meta/agileflow-metadata.json" ]; then
|
|
|
65
132
|
SHOW_EPIC=$(echo "$COMPONENTS" | jq -r '.epic | if . == null then true else . end')
|
|
66
133
|
SHOW_WIP=$(echo "$COMPONENTS" | jq -r '.wip | if . == null then true else . end')
|
|
67
134
|
SHOW_CONTEXT=$(echo "$COMPONENTS" | jq -r '.context | if . == null then true else . end')
|
|
135
|
+
SHOW_CONTEXT_BAR=$(echo "$COMPONENTS" | jq -r '.context_bar | if . == null then true else . end')
|
|
136
|
+
SHOW_SESSION_TIME=$(echo "$COMPONENTS" | jq -r '.session_time | if . == null then true else . end')
|
|
68
137
|
SHOW_COST=$(echo "$COMPONENTS" | jq -r '.cost | if . == null then true else . end')
|
|
69
138
|
SHOW_GIT=$(echo "$COMPONENTS" | jq -r '.git | if . == null then true else . end')
|
|
70
139
|
fi
|
|
@@ -132,24 +201,29 @@ CONTEXT_SIZE=$(echo "$input" | jq -r '.context_window.context_window_size // 200
|
|
|
132
201
|
USAGE=$(echo "$input" | jq '.context_window.current_usage // null')
|
|
133
202
|
|
|
134
203
|
CTX_DISPLAY=""
|
|
135
|
-
|
|
204
|
+
CTX_BAR_DISPLAY=""
|
|
205
|
+
CTX_COLOR="$CTX_GREEN"
|
|
136
206
|
if [ "$USAGE" != "null" ]; then
|
|
137
207
|
CURRENT_TOKENS=$(echo "$USAGE" | jq '.input_tokens + (.cache_creation_input_tokens // 0) + (.cache_read_input_tokens // 0)')
|
|
138
208
|
if [ "$CURRENT_TOKENS" != "null" ] && [ "$CURRENT_TOKENS" -gt 0 ] 2>/dev/null; then
|
|
139
209
|
PERCENT_USED=$((CURRENT_TOKENS * 100 / CONTEXT_SIZE))
|
|
140
210
|
|
|
141
|
-
# Color based on usage level
|
|
211
|
+
# Color based on usage level (using vibrant 256-color palette)
|
|
142
212
|
if [ "$PERCENT_USED" -ge 80 ]; then
|
|
143
|
-
CTX_COLOR="$
|
|
213
|
+
CTX_COLOR="$CTX_RED" # Coral red - critical
|
|
144
214
|
elif [ "$PERCENT_USED" -ge 60 ]; then
|
|
145
|
-
CTX_COLOR="$
|
|
215
|
+
CTX_COLOR="$CTX_ORANGE" # Peach - high usage
|
|
146
216
|
elif [ "$PERCENT_USED" -ge 40 ]; then
|
|
147
|
-
CTX_COLOR="$
|
|
217
|
+
CTX_COLOR="$CTX_YELLOW" # Peach - moderate
|
|
148
218
|
else
|
|
149
|
-
CTX_COLOR="$
|
|
219
|
+
CTX_COLOR="$CTX_GREEN" # Mint green - healthy
|
|
150
220
|
fi
|
|
151
221
|
|
|
152
222
|
CTX_DISPLAY="${CTX_COLOR}${PERCENT_USED}%${RESET}"
|
|
223
|
+
|
|
224
|
+
# Generate progress bar (8 chars wide for compactness)
|
|
225
|
+
CTX_BAR=$(progress_bar "$PERCENT_USED" 8)
|
|
226
|
+
CTX_BAR_DISPLAY="${DIM}[${RESET}${CTX_COLOR}${CTX_BAR}${RESET}${DIM}]${RESET}"
|
|
153
227
|
fi
|
|
154
228
|
fi
|
|
155
229
|
|
|
@@ -166,6 +240,67 @@ if [ "$TOTAL_COST" != "0" ] && [ "$TOTAL_COST" != "null" ]; then
|
|
|
166
240
|
fi
|
|
167
241
|
fi
|
|
168
242
|
|
|
243
|
+
# ============================================================================
|
|
244
|
+
# Session Time Remaining (via ccusage if available)
|
|
245
|
+
# ============================================================================
|
|
246
|
+
SESSION_DISPLAY=""
|
|
247
|
+
if [ "$SHOW_SESSION_TIME" = "true" ]; then
|
|
248
|
+
# Try to get session info from ccusage (fast cached check)
|
|
249
|
+
if command -v npx >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
|
250
|
+
# Use timeout to prevent blocking - ccusage should be fast
|
|
251
|
+
BLOCKS_OUTPUT=$(timeout 3 npx ccusage@latest blocks --json 2>/dev/null || true)
|
|
252
|
+
|
|
253
|
+
if [ -n "$BLOCKS_OUTPUT" ]; then
|
|
254
|
+
ACTIVE_BLOCK=$(echo "$BLOCKS_OUTPUT" | jq -c '.blocks[] | select(.isActive == true)' 2>/dev/null | head -n1)
|
|
255
|
+
|
|
256
|
+
if [ -n "$ACTIVE_BLOCK" ]; then
|
|
257
|
+
# Get reset time
|
|
258
|
+
RESET_TIME_STR=$(echo "$ACTIVE_BLOCK" | jq -r '.usageLimitResetTime // .endTime // empty')
|
|
259
|
+
START_TIME_STR=$(echo "$ACTIVE_BLOCK" | jq -r '.startTime // empty')
|
|
260
|
+
|
|
261
|
+
if [ -n "$RESET_TIME_STR" ] && [ -n "$START_TIME_STR" ]; then
|
|
262
|
+
START_SEC=$(to_epoch "$START_TIME_STR")
|
|
263
|
+
END_SEC=$(to_epoch "$RESET_TIME_STR")
|
|
264
|
+
NOW_SEC=$(date +%s)
|
|
265
|
+
|
|
266
|
+
if [ -n "$START_SEC" ] && [ -n "$END_SEC" ] && [ "$START_SEC" -gt 0 ] && [ "$END_SEC" -gt 0 ]; then
|
|
267
|
+
TOTAL=$(( END_SEC - START_SEC ))
|
|
268
|
+
[ "$TOTAL" -lt 1 ] && TOTAL=1
|
|
269
|
+
|
|
270
|
+
ELAPSED=$(( NOW_SEC - START_SEC ))
|
|
271
|
+
[ "$ELAPSED" -lt 0 ] && ELAPSED=0
|
|
272
|
+
[ "$ELAPSED" -gt "$TOTAL" ] && ELAPSED=$TOTAL
|
|
273
|
+
|
|
274
|
+
SESSION_PCT=$(( ELAPSED * 100 / TOTAL ))
|
|
275
|
+
REMAINING=$(( END_SEC - NOW_SEC ))
|
|
276
|
+
[ "$REMAINING" -lt 0 ] && REMAINING=0
|
|
277
|
+
|
|
278
|
+
# Format remaining time
|
|
279
|
+
RH=$(( REMAINING / 3600 ))
|
|
280
|
+
RM=$(( (REMAINING % 3600) / 60 ))
|
|
281
|
+
|
|
282
|
+
# Color based on time remaining (using vibrant 256-color palette)
|
|
283
|
+
if [ "$RH" -eq 0 ] && [ "$RM" -lt 30 ]; then
|
|
284
|
+
SESSION_COLOR="$SESSION_RED" # Light pink - critical
|
|
285
|
+
elif [ "$RH" -eq 0 ]; then
|
|
286
|
+
SESSION_COLOR="$SESSION_YELLOW" # Light yellow - getting low
|
|
287
|
+
else
|
|
288
|
+
SESSION_COLOR="$SESSION_GREEN" # Light green - plenty of time
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
# Build compact display: "⏱2h15m" or "⏱45m"
|
|
292
|
+
if [ "$RH" -gt 0 ]; then
|
|
293
|
+
SESSION_DISPLAY="${SESSION_COLOR}⏱${RH}h${RM}m${RESET}"
|
|
294
|
+
else
|
|
295
|
+
SESSION_DISPLAY="${SESSION_COLOR}⏱${RM}m${RESET}"
|
|
296
|
+
fi
|
|
297
|
+
fi
|
|
298
|
+
fi
|
|
299
|
+
fi
|
|
300
|
+
fi
|
|
301
|
+
fi
|
|
302
|
+
fi
|
|
303
|
+
|
|
169
304
|
# ============================================================================
|
|
170
305
|
# AgileFlow Status - Read from status.json
|
|
171
306
|
# ============================================================================
|
|
@@ -334,10 +469,21 @@ if [ "$SHOW_WIP" = "true" ] && [ -n "$WIP_DISPLAY" ]; then
|
|
|
334
469
|
OUTPUT="${OUTPUT}${WIP_DISPLAY}"
|
|
335
470
|
fi
|
|
336
471
|
|
|
337
|
-
# Add context usage (if enabled)
|
|
472
|
+
# Add context usage (if enabled) - percentage and/or bar
|
|
338
473
|
if [ "$SHOW_CONTEXT" = "true" ] && [ -n "$CTX_DISPLAY" ]; then
|
|
339
474
|
[ -n "$OUTPUT" ] && OUTPUT="${OUTPUT}${SEP}"
|
|
340
|
-
|
|
475
|
+
if [ "$SHOW_CONTEXT_BAR" = "true" ] && [ -n "$CTX_BAR_DISPLAY" ]; then
|
|
476
|
+
# Show both percentage and bar: "45% [▓▓▓▓░░░░]"
|
|
477
|
+
OUTPUT="${OUTPUT}${CTX_DISPLAY} ${CTX_BAR_DISPLAY}"
|
|
478
|
+
else
|
|
479
|
+
OUTPUT="${OUTPUT}${CTX_DISPLAY}"
|
|
480
|
+
fi
|
|
481
|
+
fi
|
|
482
|
+
|
|
483
|
+
# Add session time remaining (if enabled and available)
|
|
484
|
+
if [ "$SHOW_SESSION_TIME" = "true" ] && [ -n "$SESSION_DISPLAY" ]; then
|
|
485
|
+
[ -n "$OUTPUT" ] && OUTPUT="${OUTPUT}${SEP}"
|
|
486
|
+
OUTPUT="${OUTPUT}${SESSION_DISPLAY}"
|
|
341
487
|
fi
|
|
342
488
|
|
|
343
489
|
# Add cost (if enabled)
|