claude-limitline 1.3.0 → 1.5.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/README.md +10 -9
- package/dist/index.js +93 -83
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ Create a `claude-limitline.json` file in your Claude config directory (`~/.claud
|
|
|
123
123
|
| `weekly.displayStyle` | `"bar"` or `"text"` | `"text"` |
|
|
124
124
|
| `weekly.barWidth` | Width of progress bar in characters | `10` |
|
|
125
125
|
| `weekly.showWeekProgress` | Show week progress percentage | `true` |
|
|
126
|
-
| `weekly.viewMode` | `"simple"
|
|
126
|
+
| `weekly.viewMode` | `"simple"` or `"smart"` | `"simple"` |
|
|
127
127
|
| `budget.pollInterval` | Minutes between API calls | `15` |
|
|
128
128
|
| `budget.warningThreshold` | Percentage to trigger warning color | `80` |
|
|
129
129
|
| `theme` | Color theme name | `"dark"` |
|
|
@@ -132,15 +132,16 @@ Create a `claude-limitline.json` file in your Claude config directory (`~/.claud
|
|
|
132
132
|
|
|
133
133
|
### Weekly View Modes
|
|
134
134
|
|
|
135
|
-
The weekly segment supports
|
|
135
|
+
The weekly segment supports two view modes for displaying usage limits:
|
|
136
|
+
|
|
137
|
+

|
|
136
138
|
|
|
137
139
|
| Mode | Description | Example |
|
|
138
140
|
|------|-------------|---------|
|
|
139
141
|
| `simple` | Shows overall weekly usage only (default) | `○ 47% (wk 85%)` |
|
|
140
|
-
| `
|
|
141
|
-
| `smart` | Shows the most restrictive (bottleneck) limit with indicator | `○47%▲ (wk 85%)` |
|
|
142
|
+
| `smart` | Model-aware: shows Sonnet + Overall when using Sonnet | `◇7% \| ○47% (wk 85%)` |
|
|
142
143
|
|
|
143
|
-
**Note:** Model-specific limits (Opus/Sonnet) are only available on certain subscription tiers.
|
|
144
|
+
**Note:** Model-specific limits (Opus/Sonnet) are only available on certain subscription tiers. Smart mode shows only overall usage when using Opus/Haiku, and shows both Sonnet and overall when using Sonnet.
|
|
144
145
|
|
|
145
146
|
### Available Themes
|
|
146
147
|
|
|
@@ -190,7 +191,7 @@ npm run dev # Watch mode
|
|
|
190
191
|
|
|
191
192
|
## Testing
|
|
192
193
|
|
|
193
|
-
The project uses [Vitest](https://vitest.dev/) for testing with
|
|
194
|
+
The project uses [Vitest](https://vitest.dev/) for testing with 164 tests covering config loading, themes, segments, utilities, and rendering.
|
|
194
195
|
|
|
195
196
|
```bash
|
|
196
197
|
npm test # Run tests once
|
|
@@ -205,13 +206,13 @@ npm run test:coverage # Coverage report
|
|
|
205
206
|
| `src/config/loader.test.ts` | 7 | Config loading, merging, fallbacks |
|
|
206
207
|
| `src/themes/index.test.ts` | 37 | Theme retrieval, color validation |
|
|
207
208
|
| `src/segments/block.test.ts` | 8 | Block segment, time calculations |
|
|
208
|
-
| `src/segments/weekly.test.ts` |
|
|
209
|
-
| `src/utils/oauth.test.ts` |
|
|
209
|
+
| `src/segments/weekly.test.ts` | 13 | Weekly segment, week progress |
|
|
210
|
+
| `src/utils/oauth.test.ts` | 13 | API responses, caching, trends |
|
|
210
211
|
| `src/utils/claude-hook.test.ts` | 21 | Model name formatting |
|
|
211
212
|
| `src/utils/environment.test.ts` | 20 | Git branch, directory detection |
|
|
212
213
|
| `src/utils/terminal.test.ts` | 13 | Terminal width, ANSI handling |
|
|
213
214
|
| `src/utils/logger.test.ts` | 8 | Debug/error logging |
|
|
214
|
-
| `src/renderer.test.ts` |
|
|
215
|
+
| `src/renderer.test.ts` | 24 | Segment rendering, ordering, view modes |
|
|
215
216
|
|
|
216
217
|
## Debug Mode
|
|
217
218
|
|
package/dist/index.js
CHANGED
|
@@ -44,6 +44,9 @@ var DEFAULT_CONFIG = {
|
|
|
44
44
|
showWeekProgress: true,
|
|
45
45
|
viewMode: "simple"
|
|
46
46
|
},
|
|
47
|
+
context: {
|
|
48
|
+
enabled: true
|
|
49
|
+
},
|
|
47
50
|
budget: {
|
|
48
51
|
pollInterval: 15,
|
|
49
52
|
warningThreshold: 80
|
|
@@ -535,6 +538,8 @@ var darkTheme = {
|
|
|
535
538
|
// Purple for Opus
|
|
536
539
|
sonnet: { bg: "#1a1a1a", fg: "#89ddff" },
|
|
537
540
|
// Light blue for Sonnet
|
|
541
|
+
context: { bg: "#2a2a2a", fg: "#87ceeb" },
|
|
542
|
+
// Sky blue for context
|
|
538
543
|
warning: { bg: "#d75f00", fg: "#ffffff" },
|
|
539
544
|
critical: { bg: "#af0000", fg: "#ffffff" }
|
|
540
545
|
};
|
|
@@ -548,6 +553,8 @@ var lightTheme = {
|
|
|
548
553
|
// Purple for Opus
|
|
549
554
|
sonnet: { bg: "#0ea5e9", fg: "#ffffff" },
|
|
550
555
|
// Sky blue for Sonnet
|
|
556
|
+
context: { bg: "#6366f1", fg: "#ffffff" },
|
|
557
|
+
// Indigo for context
|
|
551
558
|
warning: { bg: "#f59e0b", fg: "#000000" },
|
|
552
559
|
critical: { bg: "#ef4444", fg: "#ffffff" }
|
|
553
560
|
};
|
|
@@ -561,6 +568,8 @@ var nordTheme = {
|
|
|
561
568
|
// Nord purple for Opus
|
|
562
569
|
sonnet: { bg: "#2e3440", fg: "#88c0d0" },
|
|
563
570
|
// Nord frost for Sonnet
|
|
571
|
+
context: { bg: "#3b4252", fg: "#81a1c1" },
|
|
572
|
+
// Nord frost for context
|
|
564
573
|
warning: { bg: "#d08770", fg: "#2e3440" },
|
|
565
574
|
critical: { bg: "#bf616a", fg: "#eceff4" }
|
|
566
575
|
};
|
|
@@ -574,6 +583,8 @@ var gruvboxTheme = {
|
|
|
574
583
|
// Gruvbox purple for Opus
|
|
575
584
|
sonnet: { bg: "#282828", fg: "#8ec07c" },
|
|
576
585
|
// Gruvbox aqua for Sonnet
|
|
586
|
+
context: { bg: "#3c3836", fg: "#83a598" },
|
|
587
|
+
// Gruvbox blue for context
|
|
577
588
|
warning: { bg: "#d79921", fg: "#282828" },
|
|
578
589
|
critical: { bg: "#cc241d", fg: "#ebdbb2" }
|
|
579
590
|
};
|
|
@@ -587,6 +598,8 @@ var tokyoNightTheme = {
|
|
|
587
598
|
// Tokyo purple for Opus
|
|
588
599
|
sonnet: { bg: "#1a202c", fg: "#7dcfff" },
|
|
589
600
|
// Tokyo cyan for Sonnet
|
|
601
|
+
context: { bg: "#2d3748", fg: "#7aa2f7" },
|
|
602
|
+
// Tokyo blue for context
|
|
590
603
|
warning: { bg: "#e0af68", fg: "#1a1b26" },
|
|
591
604
|
critical: { bg: "#f7768e", fg: "#1a1b26" }
|
|
592
605
|
};
|
|
@@ -600,6 +613,8 @@ var rosePineTheme = {
|
|
|
600
613
|
// Rose Pine iris for Opus
|
|
601
614
|
sonnet: { bg: "#232136", fg: "#31748f" },
|
|
602
615
|
// Rose Pine pine for Sonnet
|
|
616
|
+
context: { bg: "#2a273f", fg: "#9ccfd8" },
|
|
617
|
+
// Rose Pine foam for context
|
|
603
618
|
warning: { bg: "#f6c177", fg: "#191724" },
|
|
604
619
|
critical: { bg: "#eb6f92", fg: "#191724" }
|
|
605
620
|
};
|
|
@@ -639,9 +654,12 @@ var Renderer = class {
|
|
|
639
654
|
sonnet: symbolSet.sonnet_cost,
|
|
640
655
|
bottleneck: symbolSet.bottleneck,
|
|
641
656
|
rightArrow: symbolSet.right,
|
|
657
|
+
leftArrow: symbolSet.left,
|
|
642
658
|
separator: symbolSet.separator,
|
|
643
659
|
branch: symbolSet.branch,
|
|
644
660
|
model: symbolSet.model,
|
|
661
|
+
context: "\u25D0",
|
|
662
|
+
// Half-filled circle for context
|
|
645
663
|
progressFull: symbolSet.progress_full,
|
|
646
664
|
progressEmpty: symbolSet.progress_empty,
|
|
647
665
|
trendUp: "\u2191",
|
|
@@ -704,6 +722,18 @@ var Renderer = class {
|
|
|
704
722
|
output += RESET_CODE;
|
|
705
723
|
return output;
|
|
706
724
|
}
|
|
725
|
+
renderRightPowerline(segments) {
|
|
726
|
+
if (segments.length === 0) return "";
|
|
727
|
+
let output = "";
|
|
728
|
+
for (let i = 0; i < segments.length; i++) {
|
|
729
|
+
const seg = segments[i];
|
|
730
|
+
output += RESET_CODE;
|
|
731
|
+
output += ansi.fg(seg.colors.bg) + this.symbols.leftArrow;
|
|
732
|
+
output += ansi.bg(seg.colors.bg) + ansi.fg(seg.colors.fg) + seg.text;
|
|
733
|
+
}
|
|
734
|
+
output += RESET_CODE;
|
|
735
|
+
return output;
|
|
736
|
+
}
|
|
707
737
|
renderFallback(segments) {
|
|
708
738
|
return segments.map((seg) => ansi.bg(seg.colors.bg) + ansi.fg(seg.colors.fg) + seg.text + RESET_CODE).join(` ${this.symbols.separator} `);
|
|
709
739
|
}
|
|
@@ -806,94 +836,39 @@ var Renderer = class {
|
|
|
806
836
|
colors: this.theme.weekly
|
|
807
837
|
};
|
|
808
838
|
}
|
|
809
|
-
|
|
839
|
+
renderWeeklySmart(ctx) {
|
|
810
840
|
const info = ctx.weeklyInfo;
|
|
811
841
|
const overallIcon = this.usePowerline ? this.symbols.weekly : "All";
|
|
812
|
-
const opusIcon = this.usePowerline ? this.symbols.opus : "Op";
|
|
813
842
|
const sonnetIcon = this.usePowerline ? this.symbols.sonnet : "So";
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
if (parts.length === 0) {
|
|
843
|
+
const showWeekProgress = this.config.weekly?.showWeekProgress ?? true;
|
|
844
|
+
const currentModel = ctx.envInfo.model?.toLowerCase() ?? "";
|
|
845
|
+
const isSonnet = currentModel.includes("sonnet");
|
|
846
|
+
if (isSonnet && info.sonnetPercentUsed !== null && info.percentUsed !== null) {
|
|
847
|
+
const sonnetTrend = this.getTrendSymbol(ctx.trendInfo?.sevenDaySonnetTrend ?? null);
|
|
848
|
+
const overallTrend = this.getTrendSymbol(ctx.trendInfo?.sevenDayTrend ?? null);
|
|
849
|
+
let text2 = `${sonnetIcon}${Math.round(info.sonnetPercentUsed)}%${sonnetTrend} | ${overallIcon}${Math.round(info.percentUsed)}%${overallTrend}`;
|
|
850
|
+
if (showWeekProgress && !ctx.compact) {
|
|
851
|
+
text2 += ` (wk ${info.weekProgressPercent}%)`;
|
|
852
|
+
}
|
|
853
|
+
const maxPercent = Math.max(info.sonnetPercentUsed, info.percentUsed);
|
|
854
|
+
const colors2 = this.getColorsForPercent(maxPercent, this.theme.weekly);
|
|
828
855
|
return {
|
|
829
|
-
text: ` ${
|
|
830
|
-
colors:
|
|
856
|
+
text: ` ${text2} `,
|
|
857
|
+
colors: colors2
|
|
831
858
|
};
|
|
832
859
|
}
|
|
833
|
-
|
|
834
|
-
const text = parts.join(separator);
|
|
835
|
-
const maxPercent = Math.max(
|
|
836
|
-
info.percentUsed ?? 0,
|
|
837
|
-
info.opusPercentUsed ?? 0,
|
|
838
|
-
info.sonnetPercentUsed ?? 0
|
|
839
|
-
);
|
|
840
|
-
const colors = this.getColorsForPercent(maxPercent, this.theme.weekly);
|
|
841
|
-
return {
|
|
842
|
-
text: ` ${text} `,
|
|
843
|
-
colors
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
renderWeeklySmart(ctx) {
|
|
847
|
-
const info = ctx.weeklyInfo;
|
|
848
|
-
const overallIcon = this.usePowerline ? this.symbols.weekly : "All";
|
|
849
|
-
const opusIcon = this.usePowerline ? this.symbols.opus : "Op";
|
|
850
|
-
const sonnetIcon = this.usePowerline ? this.symbols.sonnet : "So";
|
|
851
|
-
const limits = [];
|
|
852
|
-
if (info.percentUsed !== null) {
|
|
853
|
-
limits.push({
|
|
854
|
-
name: "all",
|
|
855
|
-
icon: overallIcon,
|
|
856
|
-
percent: info.percentUsed,
|
|
857
|
-
trend: ctx.trendInfo?.sevenDayTrend ?? null,
|
|
858
|
-
colors: this.theme.weekly
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
if (info.opusPercentUsed !== null) {
|
|
862
|
-
limits.push({
|
|
863
|
-
name: "opus",
|
|
864
|
-
icon: opusIcon,
|
|
865
|
-
percent: info.opusPercentUsed,
|
|
866
|
-
trend: ctx.trendInfo?.sevenDayOpusTrend ?? null,
|
|
867
|
-
colors: this.theme.opus
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
|
-
if (info.sonnetPercentUsed !== null) {
|
|
871
|
-
limits.push({
|
|
872
|
-
name: "sonnet",
|
|
873
|
-
icon: sonnetIcon,
|
|
874
|
-
percent: info.sonnetPercentUsed,
|
|
875
|
-
trend: ctx.trendInfo?.sevenDaySonnetTrend ?? null,
|
|
876
|
-
colors: this.theme.sonnet
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
|
-
if (limits.length === 0) {
|
|
860
|
+
if (info.percentUsed === null) {
|
|
880
861
|
return {
|
|
881
862
|
text: ` ${overallIcon} -- `,
|
|
882
863
|
colors: this.theme.weekly
|
|
883
864
|
};
|
|
884
865
|
}
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
const bottleneckIndicator = limits.length > 1 ? this.symbols.bottleneck : "";
|
|
888
|
-
let text = `${bottleneck.icon}${Math.round(bottleneck.percent)}%${trend}`;
|
|
889
|
-
if (bottleneckIndicator && !ctx.compact) {
|
|
890
|
-
text += bottleneckIndicator;
|
|
891
|
-
}
|
|
892
|
-
const showWeekProgress = this.config.weekly?.showWeekProgress ?? true;
|
|
866
|
+
const trend = this.getTrendSymbol(ctx.trendInfo?.sevenDayTrend ?? null);
|
|
867
|
+
let text = `${overallIcon}${Math.round(info.percentUsed)}%${trend}`;
|
|
893
868
|
if (showWeekProgress && !ctx.compact) {
|
|
894
869
|
text += ` (wk ${info.weekProgressPercent}%)`;
|
|
895
870
|
}
|
|
896
|
-
const colors = this.getColorsForPercent(
|
|
871
|
+
const colors = this.getColorsForPercent(info.percentUsed, this.theme.weekly);
|
|
897
872
|
return {
|
|
898
873
|
text: ` ${text} `,
|
|
899
874
|
colors
|
|
@@ -905,8 +880,6 @@ var Renderer = class {
|
|
|
905
880
|
}
|
|
906
881
|
const viewMode = this.config.weekly?.viewMode ?? "simple";
|
|
907
882
|
switch (viewMode) {
|
|
908
|
-
case "detailed":
|
|
909
|
-
return this.renderWeeklyDetailed(ctx);
|
|
910
883
|
case "smart":
|
|
911
884
|
return this.renderWeeklySmart(ctx);
|
|
912
885
|
case "simple":
|
|
@@ -914,6 +887,18 @@ var Renderer = class {
|
|
|
914
887
|
return this.renderWeeklySimple(ctx);
|
|
915
888
|
}
|
|
916
889
|
}
|
|
890
|
+
renderContext(ctx) {
|
|
891
|
+
if (!this.config.context?.enabled) {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
const percent = ctx.envInfo.contextPercent;
|
|
895
|
+
const icon = this.usePowerline ? this.symbols.context : "CTX";
|
|
896
|
+
const colors = this.getColorsForPercent(percent, this.theme.context);
|
|
897
|
+
return {
|
|
898
|
+
text: ` ${icon} ${percent}% `,
|
|
899
|
+
colors
|
|
900
|
+
};
|
|
901
|
+
}
|
|
917
902
|
getSegment(name, ctx) {
|
|
918
903
|
switch (name) {
|
|
919
904
|
case "directory":
|
|
@@ -926,6 +911,8 @@ var Renderer = class {
|
|
|
926
911
|
return this.renderBlock(ctx);
|
|
927
912
|
case "weekly":
|
|
928
913
|
return this.renderWeekly(ctx);
|
|
914
|
+
case "context":
|
|
915
|
+
return this.renderContext(ctx);
|
|
929
916
|
default:
|
|
930
917
|
return null;
|
|
931
918
|
}
|
|
@@ -939,22 +926,35 @@ var Renderer = class {
|
|
|
939
926
|
trendInfo,
|
|
940
927
|
compact
|
|
941
928
|
};
|
|
942
|
-
const
|
|
929
|
+
const leftSegments = [];
|
|
943
930
|
const order = this.config.segmentOrder ?? ["directory", "git", "model", "block", "weekly"];
|
|
944
931
|
for (const name of order) {
|
|
932
|
+
if (name === "context") continue;
|
|
945
933
|
const segment = this.getSegment(name, ctx);
|
|
946
934
|
if (segment) {
|
|
947
|
-
|
|
935
|
+
leftSegments.push(segment);
|
|
948
936
|
}
|
|
949
937
|
}
|
|
950
|
-
|
|
951
|
-
|
|
938
|
+
const rightSegments = [];
|
|
939
|
+
const contextSegment = this.renderContext(ctx);
|
|
940
|
+
if (contextSegment) {
|
|
941
|
+
rightSegments.push(contextSegment);
|
|
952
942
|
}
|
|
943
|
+
let output = "";
|
|
953
944
|
if (this.usePowerline) {
|
|
954
|
-
|
|
945
|
+
if (leftSegments.length > 0) {
|
|
946
|
+
output += this.renderPowerline(leftSegments);
|
|
947
|
+
}
|
|
948
|
+
if (rightSegments.length > 0) {
|
|
949
|
+
output += this.renderRightPowerline(rightSegments);
|
|
950
|
+
}
|
|
955
951
|
} else {
|
|
956
|
-
|
|
952
|
+
const allSegments = [...leftSegments, ...rightSegments];
|
|
953
|
+
if (allSegments.length > 0) {
|
|
954
|
+
output = this.renderFallback(allSegments);
|
|
955
|
+
}
|
|
957
956
|
}
|
|
957
|
+
return output;
|
|
958
958
|
}
|
|
959
959
|
};
|
|
960
960
|
|
|
@@ -1080,12 +1080,22 @@ function getClaudeModel(hookData) {
|
|
|
1080
1080
|
}
|
|
1081
1081
|
return null;
|
|
1082
1082
|
}
|
|
1083
|
+
function getContextPercent(hookData) {
|
|
1084
|
+
const ctx = hookData?.context_window;
|
|
1085
|
+
if (!ctx?.current_usage || !ctx.context_window_size) {
|
|
1086
|
+
return 0;
|
|
1087
|
+
}
|
|
1088
|
+
const usage = ctx.current_usage;
|
|
1089
|
+
const totalTokens = (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
|
|
1090
|
+
return Math.round(totalTokens / ctx.context_window_size * 100);
|
|
1091
|
+
}
|
|
1083
1092
|
function getEnvironmentInfo(hookData) {
|
|
1084
1093
|
return {
|
|
1085
1094
|
directory: getDirectoryName(hookData),
|
|
1086
1095
|
gitBranch: getGitBranch(),
|
|
1087
1096
|
gitDirty: hasGitChanges(),
|
|
1088
|
-
model: getClaudeModel(hookData)
|
|
1097
|
+
model: getClaudeModel(hookData),
|
|
1098
|
+
contextPercent: getContextPercent(hookData)
|
|
1089
1099
|
};
|
|
1090
1100
|
}
|
|
1091
1101
|
|