claude-limitline 1.4.0 → 1.5.1
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 +7 -3
- package/dist/index.js +92 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -173,12 +173,16 @@ claude-limitline retrieves data from two sources:
|
|
|
173
173
|
|
|
174
174
|
### OAuth Token Location
|
|
175
175
|
|
|
176
|
+
The tool automatically retrieves your OAuth token from Claude Code's credential storage:
|
|
177
|
+
|
|
176
178
|
| Platform | Location |
|
|
177
179
|
|----------|----------|
|
|
180
|
+
| **macOS** | Keychain (`Claude Code-credentials` service) |
|
|
178
181
|
| **Windows** | Credential Manager or `~/.claude/.credentials.json` |
|
|
179
|
-
| **macOS** | Keychain or `~/.claude/.credentials.json` |
|
|
180
182
|
| **Linux** | secret-tool (GNOME Keyring) or `~/.claude/.credentials.json` |
|
|
181
183
|
|
|
184
|
+
> **Note:** On macOS, the token is read directly from the system Keychain where Claude Code stores it. No additional configuration is needed—just make sure you're logged into Claude Code (`claude --login`).
|
|
185
|
+
|
|
182
186
|
## Development
|
|
183
187
|
|
|
184
188
|
```bash
|
|
@@ -191,7 +195,7 @@ npm run dev # Watch mode
|
|
|
191
195
|
|
|
192
196
|
## Testing
|
|
193
197
|
|
|
194
|
-
The project uses [Vitest](https://vitest.dev/) for testing with
|
|
198
|
+
The project uses [Vitest](https://vitest.dev/) for testing with 170 tests covering config loading, themes, segments, utilities, and rendering.
|
|
195
199
|
|
|
196
200
|
```bash
|
|
197
201
|
npm test # Run tests once
|
|
@@ -207,7 +211,7 @@ npm run test:coverage # Coverage report
|
|
|
207
211
|
| `src/themes/index.test.ts` | 37 | Theme retrieval, color validation |
|
|
208
212
|
| `src/segments/block.test.ts` | 8 | Block segment, time calculations |
|
|
209
213
|
| `src/segments/weekly.test.ts` | 13 | Weekly segment, week progress |
|
|
210
|
-
| `src/utils/oauth.test.ts` |
|
|
214
|
+
| `src/utils/oauth.test.ts` | 19 | API responses, caching, trends, macOS keychain |
|
|
211
215
|
| `src/utils/claude-hook.test.ts` | 21 | Model name formatting |
|
|
212
216
|
| `src/utils/environment.test.ts` | 20 | Git branch, directory detection |
|
|
213
217
|
| `src/utils/terminal.test.ts` | 13 | Terminal width, ANSI handling |
|
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
|
|
@@ -161,12 +164,26 @@ async function getOAuthTokenWindows() {
|
|
|
161
164
|
async function getOAuthTokenMacOS() {
|
|
162
165
|
try {
|
|
163
166
|
const { stdout } = await execAsync(
|
|
164
|
-
`security find-generic-password -s "Claude Code" -w`,
|
|
167
|
+
`security find-generic-password -s "Claude Code-credentials" -w`,
|
|
165
168
|
{ timeout: 5e3 }
|
|
166
169
|
);
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
+
const content = stdout.trim();
|
|
171
|
+
if (content.startsWith("{")) {
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(content);
|
|
174
|
+
if (parsed.claudeAiOauth && typeof parsed.claudeAiOauth === "object") {
|
|
175
|
+
const token = parsed.claudeAiOauth.accessToken;
|
|
176
|
+
if (token && typeof token === "string" && token.startsWith("sk-ant-oat")) {
|
|
177
|
+
debug("Found OAuth token in macOS Keychain under claudeAiOauth.accessToken");
|
|
178
|
+
return token;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (parseError) {
|
|
182
|
+
debug("Failed to parse keychain JSON:", parseError);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (content.startsWith("sk-ant-oat")) {
|
|
186
|
+
return content;
|
|
170
187
|
}
|
|
171
188
|
} catch (error) {
|
|
172
189
|
debug("macOS Keychain retrieval failed:", error);
|
|
@@ -535,6 +552,8 @@ var darkTheme = {
|
|
|
535
552
|
// Purple for Opus
|
|
536
553
|
sonnet: { bg: "#1a1a1a", fg: "#89ddff" },
|
|
537
554
|
// Light blue for Sonnet
|
|
555
|
+
context: { bg: "#2a2a2a", fg: "#87ceeb" },
|
|
556
|
+
// Sky blue for context
|
|
538
557
|
warning: { bg: "#d75f00", fg: "#ffffff" },
|
|
539
558
|
critical: { bg: "#af0000", fg: "#ffffff" }
|
|
540
559
|
};
|
|
@@ -548,6 +567,8 @@ var lightTheme = {
|
|
|
548
567
|
// Purple for Opus
|
|
549
568
|
sonnet: { bg: "#0ea5e9", fg: "#ffffff" },
|
|
550
569
|
// Sky blue for Sonnet
|
|
570
|
+
context: { bg: "#6366f1", fg: "#ffffff" },
|
|
571
|
+
// Indigo for context
|
|
551
572
|
warning: { bg: "#f59e0b", fg: "#000000" },
|
|
552
573
|
critical: { bg: "#ef4444", fg: "#ffffff" }
|
|
553
574
|
};
|
|
@@ -561,6 +582,8 @@ var nordTheme = {
|
|
|
561
582
|
// Nord purple for Opus
|
|
562
583
|
sonnet: { bg: "#2e3440", fg: "#88c0d0" },
|
|
563
584
|
// Nord frost for Sonnet
|
|
585
|
+
context: { bg: "#3b4252", fg: "#81a1c1" },
|
|
586
|
+
// Nord frost for context
|
|
564
587
|
warning: { bg: "#d08770", fg: "#2e3440" },
|
|
565
588
|
critical: { bg: "#bf616a", fg: "#eceff4" }
|
|
566
589
|
};
|
|
@@ -574,6 +597,8 @@ var gruvboxTheme = {
|
|
|
574
597
|
// Gruvbox purple for Opus
|
|
575
598
|
sonnet: { bg: "#282828", fg: "#8ec07c" },
|
|
576
599
|
// Gruvbox aqua for Sonnet
|
|
600
|
+
context: { bg: "#3c3836", fg: "#83a598" },
|
|
601
|
+
// Gruvbox blue for context
|
|
577
602
|
warning: { bg: "#d79921", fg: "#282828" },
|
|
578
603
|
critical: { bg: "#cc241d", fg: "#ebdbb2" }
|
|
579
604
|
};
|
|
@@ -587,6 +612,8 @@ var tokyoNightTheme = {
|
|
|
587
612
|
// Tokyo purple for Opus
|
|
588
613
|
sonnet: { bg: "#1a202c", fg: "#7dcfff" },
|
|
589
614
|
// Tokyo cyan for Sonnet
|
|
615
|
+
context: { bg: "#2d3748", fg: "#7aa2f7" },
|
|
616
|
+
// Tokyo blue for context
|
|
590
617
|
warning: { bg: "#e0af68", fg: "#1a1b26" },
|
|
591
618
|
critical: { bg: "#f7768e", fg: "#1a1b26" }
|
|
592
619
|
};
|
|
@@ -600,6 +627,8 @@ var rosePineTheme = {
|
|
|
600
627
|
// Rose Pine iris for Opus
|
|
601
628
|
sonnet: { bg: "#232136", fg: "#31748f" },
|
|
602
629
|
// Rose Pine pine for Sonnet
|
|
630
|
+
context: { bg: "#2a273f", fg: "#9ccfd8" },
|
|
631
|
+
// Rose Pine foam for context
|
|
603
632
|
warning: { bg: "#f6c177", fg: "#191724" },
|
|
604
633
|
critical: { bg: "#eb6f92", fg: "#191724" }
|
|
605
634
|
};
|
|
@@ -639,9 +668,12 @@ var Renderer = class {
|
|
|
639
668
|
sonnet: symbolSet.sonnet_cost,
|
|
640
669
|
bottleneck: symbolSet.bottleneck,
|
|
641
670
|
rightArrow: symbolSet.right,
|
|
671
|
+
leftArrow: symbolSet.left,
|
|
642
672
|
separator: symbolSet.separator,
|
|
643
673
|
branch: symbolSet.branch,
|
|
644
674
|
model: symbolSet.model,
|
|
675
|
+
context: "\u25D0",
|
|
676
|
+
// Half-filled circle for context
|
|
645
677
|
progressFull: symbolSet.progress_full,
|
|
646
678
|
progressEmpty: symbolSet.progress_empty,
|
|
647
679
|
trendUp: "\u2191",
|
|
@@ -704,6 +736,18 @@ var Renderer = class {
|
|
|
704
736
|
output += RESET_CODE;
|
|
705
737
|
return output;
|
|
706
738
|
}
|
|
739
|
+
renderRightPowerline(segments) {
|
|
740
|
+
if (segments.length === 0) return "";
|
|
741
|
+
let output = "";
|
|
742
|
+
for (let i = 0; i < segments.length; i++) {
|
|
743
|
+
const seg = segments[i];
|
|
744
|
+
output += RESET_CODE;
|
|
745
|
+
output += ansi.fg(seg.colors.bg) + this.symbols.leftArrow;
|
|
746
|
+
output += ansi.bg(seg.colors.bg) + ansi.fg(seg.colors.fg) + seg.text;
|
|
747
|
+
}
|
|
748
|
+
output += RESET_CODE;
|
|
749
|
+
return output;
|
|
750
|
+
}
|
|
707
751
|
renderFallback(segments) {
|
|
708
752
|
return segments.map((seg) => ansi.bg(seg.colors.bg) + ansi.fg(seg.colors.fg) + seg.text + RESET_CODE).join(` ${this.symbols.separator} `);
|
|
709
753
|
}
|
|
@@ -857,6 +901,18 @@ var Renderer = class {
|
|
|
857
901
|
return this.renderWeeklySimple(ctx);
|
|
858
902
|
}
|
|
859
903
|
}
|
|
904
|
+
renderContext(ctx) {
|
|
905
|
+
if (!this.config.context?.enabled) {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
const percent = ctx.envInfo.contextPercent;
|
|
909
|
+
const icon = this.usePowerline ? this.symbols.context : "CTX";
|
|
910
|
+
const colors = this.getColorsForPercent(percent, this.theme.context);
|
|
911
|
+
return {
|
|
912
|
+
text: ` ${icon} ${percent}% `,
|
|
913
|
+
colors
|
|
914
|
+
};
|
|
915
|
+
}
|
|
860
916
|
getSegment(name, ctx) {
|
|
861
917
|
switch (name) {
|
|
862
918
|
case "directory":
|
|
@@ -869,6 +925,8 @@ var Renderer = class {
|
|
|
869
925
|
return this.renderBlock(ctx);
|
|
870
926
|
case "weekly":
|
|
871
927
|
return this.renderWeekly(ctx);
|
|
928
|
+
case "context":
|
|
929
|
+
return this.renderContext(ctx);
|
|
872
930
|
default:
|
|
873
931
|
return null;
|
|
874
932
|
}
|
|
@@ -882,22 +940,35 @@ var Renderer = class {
|
|
|
882
940
|
trendInfo,
|
|
883
941
|
compact
|
|
884
942
|
};
|
|
885
|
-
const
|
|
943
|
+
const leftSegments = [];
|
|
886
944
|
const order = this.config.segmentOrder ?? ["directory", "git", "model", "block", "weekly"];
|
|
887
945
|
for (const name of order) {
|
|
946
|
+
if (name === "context") continue;
|
|
888
947
|
const segment = this.getSegment(name, ctx);
|
|
889
948
|
if (segment) {
|
|
890
|
-
|
|
949
|
+
leftSegments.push(segment);
|
|
891
950
|
}
|
|
892
951
|
}
|
|
893
|
-
|
|
894
|
-
|
|
952
|
+
const rightSegments = [];
|
|
953
|
+
const contextSegment = this.renderContext(ctx);
|
|
954
|
+
if (contextSegment) {
|
|
955
|
+
rightSegments.push(contextSegment);
|
|
895
956
|
}
|
|
957
|
+
let output = "";
|
|
896
958
|
if (this.usePowerline) {
|
|
897
|
-
|
|
959
|
+
if (leftSegments.length > 0) {
|
|
960
|
+
output += this.renderPowerline(leftSegments);
|
|
961
|
+
}
|
|
962
|
+
if (rightSegments.length > 0) {
|
|
963
|
+
output += this.renderRightPowerline(rightSegments);
|
|
964
|
+
}
|
|
898
965
|
} else {
|
|
899
|
-
|
|
966
|
+
const allSegments = [...leftSegments, ...rightSegments];
|
|
967
|
+
if (allSegments.length > 0) {
|
|
968
|
+
output = this.renderFallback(allSegments);
|
|
969
|
+
}
|
|
900
970
|
}
|
|
971
|
+
return output;
|
|
901
972
|
}
|
|
902
973
|
};
|
|
903
974
|
|
|
@@ -1023,12 +1094,22 @@ function getClaudeModel(hookData) {
|
|
|
1023
1094
|
}
|
|
1024
1095
|
return null;
|
|
1025
1096
|
}
|
|
1097
|
+
function getContextPercent(hookData) {
|
|
1098
|
+
const ctx = hookData?.context_window;
|
|
1099
|
+
if (!ctx?.current_usage || !ctx.context_window_size) {
|
|
1100
|
+
return 0;
|
|
1101
|
+
}
|
|
1102
|
+
const usage = ctx.current_usage;
|
|
1103
|
+
const totalTokens = (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
|
|
1104
|
+
return Math.round(totalTokens / ctx.context_window_size * 100);
|
|
1105
|
+
}
|
|
1026
1106
|
function getEnvironmentInfo(hookData) {
|
|
1027
1107
|
return {
|
|
1028
1108
|
directory: getDirectoryName(hookData),
|
|
1029
1109
|
gitBranch: getGitBranch(),
|
|
1030
1110
|
gitDirty: hasGitChanges(),
|
|
1031
|
-
model: getClaudeModel(hookData)
|
|
1111
|
+
model: getClaudeModel(hookData),
|
|
1112
|
+
contextPercent: getContextPercent(hookData)
|
|
1032
1113
|
};
|
|
1033
1114
|
}
|
|
1034
1115
|
|