ccstatusline 2.2.10 โ 2.2.11
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 +5 -3
- package/dist/ccstatusline.js +327 -40
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
## ๐ Recent Updates
|
|
49
49
|
|
|
50
|
-
### v2.2.9 - v2.2.
|
|
50
|
+
### v2.2.9 - v2.2.11 - GitLab support, reset timers, context, compaction, and git widgets
|
|
51
51
|
|
|
52
52
|
- **๐ฆ GitLab PR/MR support** - `Git Branch` and `Git PR/MR` now support GitHub, GitLab, and compatible self-hosted remotes, using `gh` or `glab` as appropriate.
|
|
53
53
|
- **๐ Status line refresh interval** - Installed configs can set Claude Code's `statusLine.refreshInterval` from the TUI when Claude Code >=2.1.97 supports it.
|
|
@@ -56,10 +56,12 @@
|
|
|
56
56
|
- **๐ Short bar display modes** - Context percentage, Context Bar, Session Usage, Weekly Usage, Block Timer, and reset timer widgets can use compact bar variants.
|
|
57
57
|
- **๐ Reset timer timestamps** - Block and Weekly Reset Timer widgets can show exact reset timestamps with compact formatting, 12/24-hour display, IANA time zones, and locale selection.
|
|
58
58
|
- **๐ช Context Window widget** - Added a `Context Window` widget for total model window size, keeping `Context Length` focused on current context usage.
|
|
59
|
+
- **๐ Compaction Counter widget** - Added a `Compaction Counter` widget that tracks session context compactions, with icon/text/number formats, optional Nerd Font icon, and hide-when-zero behavior.
|
|
59
60
|
- **๐งฎ Git file status widgets** - Added `Git Staged Files`, `Git Unstaged Files`, `Git Untracked Files`, and `Git Clean Status` for file counts and clean/dirty state.
|
|
60
|
-
- **๐ง Thinking Effort updates** - Added `xhigh`, show `default` when no effort is set,
|
|
61
|
+
- **๐ง Thinking Effort updates** - Added `xhigh`, show `default` when no effort is set, mark unknown future effort levels with `?`, and track live status JSON plus `/effort` command changes.
|
|
61
62
|
- **๐งฎ More accurate token counts** - Streaming duplicate JSONL entries are deduped so token widgets do not overcount live Claude Code output.
|
|
62
63
|
- **๐ท๏ธ Cleaner model display** - The Model widget strips trailing context suffixes like `(1M context)`; use `Context Window` when you want the total window size shown.
|
|
64
|
+
- **๐งน Cleaner empty-widget separators** - Manual separators now collapse around widgets that render empty, avoiding dangling separators when hide-when-empty widgets disappear.
|
|
63
65
|
- **๐งฑ More resilient Git helpers** - Git widgets handle missing or unusual git command output more defensively.
|
|
64
66
|
|
|
65
67
|
### v2.2.8 - Git widgets, smarter picker search, and minimalist mode
|
|
@@ -192,7 +194,7 @@
|
|
|
192
194
|
|
|
193
195
|
## โจ Features
|
|
194
196
|
|
|
195
|
-
- **๐ Real-time Metrics** - Display model name, git branch, token usage, session duration, block timer, and more
|
|
197
|
+
- **๐ Real-time Metrics** - Display model name, git branch, token usage, session duration, compaction count, block timer, and more
|
|
196
198
|
- **๐จ Fully Customizable** - Choose what to display and customize colors for each element
|
|
197
199
|
- **โก Powerline Support** - Beautiful Powerline-style rendering with arrow separators, caps, and custom fonts
|
|
198
200
|
- **๐ Multi-line Support** - Configure multiple independent status lines
|
package/dist/ccstatusline.js
CHANGED
|
@@ -56010,17 +56010,26 @@ function getContextConfig(modelIdentifier, contextWindowSize) {
|
|
|
56010
56010
|
var DEFAULT_CONTEXT_WINDOW_SIZE = 200000, USABLE_CONTEXT_RATIO = 0.8;
|
|
56011
56011
|
|
|
56012
56012
|
// src/utils/context-percentage.ts
|
|
56013
|
-
function
|
|
56013
|
+
function calculateContextPercentageMetrics(context) {
|
|
56014
56014
|
const contextWindowMetrics = getContextWindowMetrics(context.data);
|
|
56015
|
+
const modelIdentifier = getModelContextIdentifier(context.data?.model);
|
|
56016
|
+
const contextConfig = getContextConfig(modelIdentifier, contextWindowMetrics.windowSize);
|
|
56015
56017
|
if (contextWindowMetrics.usedPercentage !== null) {
|
|
56016
|
-
return
|
|
56018
|
+
return {
|
|
56019
|
+
usedPercentage: contextWindowMetrics.usedPercentage,
|
|
56020
|
+
windowSize: contextConfig.maxTokens
|
|
56021
|
+
};
|
|
56017
56022
|
}
|
|
56018
56023
|
if (!context.tokenMetrics) {
|
|
56019
|
-
return
|
|
56024
|
+
return null;
|
|
56020
56025
|
}
|
|
56021
|
-
|
|
56022
|
-
|
|
56023
|
-
|
|
56026
|
+
return {
|
|
56027
|
+
usedPercentage: Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100),
|
|
56028
|
+
windowSize: contextConfig.maxTokens
|
|
56029
|
+
};
|
|
56030
|
+
}
|
|
56031
|
+
function calculateContextPercentage(context) {
|
|
56032
|
+
return calculateContextPercentageMetrics(context)?.usedPercentage ?? 0;
|
|
56024
56033
|
}
|
|
56025
56034
|
var init_context_percentage = () => {};
|
|
56026
56035
|
|
|
@@ -56127,7 +56136,7 @@ function getTerminalWidth() {
|
|
|
56127
56136
|
function canDetectTerminalWidth() {
|
|
56128
56137
|
return probeTerminalWidth() !== null;
|
|
56129
56138
|
}
|
|
56130
|
-
var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src/utils", PACKAGE_VERSION = "2.2.
|
|
56139
|
+
var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src/utils", PACKAGE_VERSION = "2.2.11";
|
|
56131
56140
|
var init_terminal = () => {};
|
|
56132
56141
|
|
|
56133
56142
|
// src/utils/renderer.ts
|
|
@@ -56532,12 +56541,12 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
56532
56541
|
let hasContentBefore = false;
|
|
56533
56542
|
for (let j = i - 1;j >= 0; j--) {
|
|
56534
56543
|
const prevWidget = widgets[j];
|
|
56535
|
-
if (prevWidget
|
|
56536
|
-
|
|
56537
|
-
|
|
56538
|
-
|
|
56539
|
-
|
|
56540
|
-
|
|
56544
|
+
if (!prevWidget)
|
|
56545
|
+
continue;
|
|
56546
|
+
if (prevWidget.type === "separator" || prevWidget.type === "flex-separator")
|
|
56547
|
+
continue;
|
|
56548
|
+
hasContentBefore = Boolean(preRenderedWidgets[j]?.content);
|
|
56549
|
+
break;
|
|
56541
56550
|
}
|
|
56542
56551
|
if (!hasContentBefore)
|
|
56543
56552
|
continue;
|
|
@@ -57378,7 +57387,7 @@ class ContextPercentageWidget {
|
|
|
57378
57387
|
render(item, context, settings) {
|
|
57379
57388
|
const isInverse = isContextInverse(item);
|
|
57380
57389
|
const sliderMode = getContextSliderMode(item);
|
|
57381
|
-
const
|
|
57390
|
+
const contextPercentageMetrics = calculateContextPercentageMetrics(context);
|
|
57382
57391
|
if (context.isPreview) {
|
|
57383
57392
|
const previewPercent = isInverse ? 90.7 : 9.3;
|
|
57384
57393
|
const sliderResult = renderContextSlider(sliderMode, previewPercent);
|
|
@@ -57387,19 +57396,8 @@ class ContextPercentageWidget {
|
|
|
57387
57396
|
}
|
|
57388
57397
|
return formatRawOrLabeledValue(item, "Ctx: ", `${previewPercent.toFixed(1)}%`);
|
|
57389
57398
|
}
|
|
57390
|
-
if (
|
|
57391
|
-
const displayPercentage = isInverse ? 100 -
|
|
57392
|
-
const sliderResult = renderContextSlider(sliderMode, displayPercentage);
|
|
57393
|
-
if (sliderResult !== null) {
|
|
57394
|
-
return formatRawOrLabeledValue(item, "Ctx: ", sliderResult);
|
|
57395
|
-
}
|
|
57396
|
-
return formatRawOrLabeledValue(item, "Ctx: ", `${displayPercentage.toFixed(1)}%`);
|
|
57397
|
-
}
|
|
57398
|
-
if (context.tokenMetrics) {
|
|
57399
|
-
const modelIdentifier = getModelContextIdentifier(context.data?.model);
|
|
57400
|
-
const contextConfig = getContextConfig(modelIdentifier, contextWindowMetrics.windowSize);
|
|
57401
|
-
const usedPercentage = Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100);
|
|
57402
|
-
const displayPercentage = isInverse ? 100 - usedPercentage : usedPercentage;
|
|
57399
|
+
if (contextPercentageMetrics !== null) {
|
|
57400
|
+
const displayPercentage = isInverse ? 100 - contextPercentageMetrics.usedPercentage : contextPercentageMetrics.usedPercentage;
|
|
57403
57401
|
const sliderResult = renderContextSlider(sliderMode, displayPercentage);
|
|
57404
57402
|
if (sliderResult !== null) {
|
|
57405
57403
|
return formatRawOrLabeledValue(item, "Ctx: ", sliderResult);
|
|
@@ -57422,6 +57420,7 @@ class ContextPercentageWidget {
|
|
|
57422
57420
|
}
|
|
57423
57421
|
}
|
|
57424
57422
|
var init_ContextPercentage = __esm(() => {
|
|
57423
|
+
init_context_percentage();
|
|
57425
57424
|
init_context_inverse();
|
|
57426
57425
|
init_context_slider();
|
|
57427
57426
|
});
|
|
@@ -63063,6 +63062,12 @@ function getTranscriptThinkingEffort(transcriptPath) {
|
|
|
63063
63062
|
continue;
|
|
63064
63063
|
}
|
|
63065
63064
|
const visibleContent = getVisibleText(entry.message.content).trim();
|
|
63065
|
+
if (visibleContent.startsWith(EFFORT_STDOUT_PREFIX)) {
|
|
63066
|
+
const effortMatch = EFFORT_STDOUT_REGEX.exec(visibleContent);
|
|
63067
|
+
if (effortMatch) {
|
|
63068
|
+
return normalizeThinkingEffort(effortMatch[1]);
|
|
63069
|
+
}
|
|
63070
|
+
}
|
|
63066
63071
|
if (!visibleContent.startsWith(MODEL_STDOUT_PREFIX)) {
|
|
63067
63072
|
continue;
|
|
63068
63073
|
}
|
|
@@ -63074,13 +63079,14 @@ function getTranscriptThinkingEffort(transcriptPath) {
|
|
|
63074
63079
|
}
|
|
63075
63080
|
return;
|
|
63076
63081
|
}
|
|
63077
|
-
var KNOWN_THINKING_EFFORTS, KNOWN_THINKING_EFFORTS_SET, MODEL_STDOUT_PREFIX = "<local-command-stdout>Set model to ", MODEL_STDOUT_EFFORT_REGEX, UNKNOWN_EFFORT_PATTERN;
|
|
63082
|
+
var KNOWN_THINKING_EFFORTS, KNOWN_THINKING_EFFORTS_SET, MODEL_STDOUT_PREFIX = "<local-command-stdout>Set model to ", MODEL_STDOUT_EFFORT_REGEX, EFFORT_STDOUT_PREFIX = "<local-command-stdout>Set effort level to ", EFFORT_STDOUT_REGEX, UNKNOWN_EFFORT_PATTERN;
|
|
63078
63083
|
var init_jsonl_metadata = __esm(() => {
|
|
63079
63084
|
init_ansi();
|
|
63080
63085
|
init_jsonl_lines();
|
|
63081
63086
|
KNOWN_THINKING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
63082
63087
|
KNOWN_THINKING_EFFORTS_SET = new Set(KNOWN_THINKING_EFFORTS);
|
|
63083
63088
|
MODEL_STDOUT_EFFORT_REGEX = /^<local-command-stdout>Set model to[\s\S]*? with ([a-zA-Z0-9-]+) effort<\/local-command-stdout>$/i;
|
|
63089
|
+
EFFORT_STDOUT_REGEX = /^<local-command-stdout>Set effort level to ([a-zA-Z0-9-]+)\b/i;
|
|
63084
63090
|
UNKNOWN_EFFORT_PATTERN = /^(?=.*[a-z0-9])[a-z0-9-]{2,20}$/;
|
|
63085
63091
|
});
|
|
63086
63092
|
|
|
@@ -65587,6 +65593,13 @@ var init_Skills = __esm(async () => {
|
|
|
65587
65593
|
});
|
|
65588
65594
|
|
|
65589
65595
|
// src/widgets/ThinkingEffort.ts
|
|
65596
|
+
function resolveThinkingEffortFromStatusJson(context) {
|
|
65597
|
+
const effort = context.data?.effort;
|
|
65598
|
+
if (!effort || !("level" in effort)) {
|
|
65599
|
+
return;
|
|
65600
|
+
}
|
|
65601
|
+
return typeof effort.level === "string" ? normalizeThinkingEffort(effort.level) : null;
|
|
65602
|
+
}
|
|
65590
65603
|
function resolveThinkingEffortFromSettings() {
|
|
65591
65604
|
try {
|
|
65592
65605
|
const settings = loadClaudeSettingsSync({ logErrors: false });
|
|
@@ -65595,6 +65608,10 @@ function resolveThinkingEffortFromSettings() {
|
|
|
65595
65608
|
return;
|
|
65596
65609
|
}
|
|
65597
65610
|
function resolveThinkingEffort(context) {
|
|
65611
|
+
const statusEffort = resolveThinkingEffortFromStatusJson(context);
|
|
65612
|
+
if (statusEffort !== undefined) {
|
|
65613
|
+
return statusEffort;
|
|
65614
|
+
}
|
|
65598
65615
|
return getTranscriptThinkingEffort(context.data?.transcript_path) ?? resolveThinkingEffortFromSettings() ?? null;
|
|
65599
65616
|
}
|
|
65600
65617
|
function formatEffort(resolved) {
|
|
@@ -65893,6 +65910,149 @@ class GitWorktreeOriginalBranchWidget {
|
|
|
65893
65910
|
}
|
|
65894
65911
|
}
|
|
65895
65912
|
|
|
65913
|
+
// src/widgets/CompactionCounter.ts
|
|
65914
|
+
function getFormat2(item) {
|
|
65915
|
+
const format = item.metadata?.format;
|
|
65916
|
+
return FORMATS2.includes(format ?? "") ? format : DEFAULT_FORMAT2;
|
|
65917
|
+
}
|
|
65918
|
+
function removeNerdFont(item) {
|
|
65919
|
+
const { [NERD_FONT_METADATA_KEY2]: removedNerdFont, ...restMetadata } = item.metadata ?? {};
|
|
65920
|
+
return {
|
|
65921
|
+
...item,
|
|
65922
|
+
metadata: Object.keys(restMetadata).length > 0 ? restMetadata : undefined
|
|
65923
|
+
};
|
|
65924
|
+
}
|
|
65925
|
+
function setFormat2(item, format) {
|
|
65926
|
+
if (format === DEFAULT_FORMAT2) {
|
|
65927
|
+
const { format: removedFormat, ...restMetadata2 } = item.metadata ?? {};
|
|
65928
|
+
return {
|
|
65929
|
+
...item,
|
|
65930
|
+
metadata: Object.keys(restMetadata2).length > 0 ? restMetadata2 : undefined
|
|
65931
|
+
};
|
|
65932
|
+
}
|
|
65933
|
+
const { [NERD_FONT_METADATA_KEY2]: removedNerdFont, ...restMetadata } = item.metadata ?? {};
|
|
65934
|
+
return {
|
|
65935
|
+
...item,
|
|
65936
|
+
metadata: {
|
|
65937
|
+
...restMetadata,
|
|
65938
|
+
format
|
|
65939
|
+
}
|
|
65940
|
+
};
|
|
65941
|
+
}
|
|
65942
|
+
function isNerdFontEnabled2(item) {
|
|
65943
|
+
return item.metadata?.[NERD_FONT_METADATA_KEY2] === "true" && getFormat2(item) === DEFAULT_FORMAT2;
|
|
65944
|
+
}
|
|
65945
|
+
function isHideZeroEnabled(item) {
|
|
65946
|
+
return item.metadata?.[HIDE_ZERO_METADATA_KEY] === "true";
|
|
65947
|
+
}
|
|
65948
|
+
function toggleHideZero(item) {
|
|
65949
|
+
return {
|
|
65950
|
+
...item,
|
|
65951
|
+
metadata: {
|
|
65952
|
+
...item.metadata ?? {},
|
|
65953
|
+
[HIDE_ZERO_METADATA_KEY]: (!isHideZeroEnabled(item)).toString()
|
|
65954
|
+
}
|
|
65955
|
+
};
|
|
65956
|
+
}
|
|
65957
|
+
function toggleNerdFont2(item) {
|
|
65958
|
+
if (getFormat2(item) !== DEFAULT_FORMAT2) {
|
|
65959
|
+
return removeNerdFont(item);
|
|
65960
|
+
}
|
|
65961
|
+
if (!isNerdFontEnabled2(item)) {
|
|
65962
|
+
return {
|
|
65963
|
+
...item,
|
|
65964
|
+
metadata: {
|
|
65965
|
+
...item.metadata ?? {},
|
|
65966
|
+
[NERD_FONT_METADATA_KEY2]: "true"
|
|
65967
|
+
}
|
|
65968
|
+
};
|
|
65969
|
+
}
|
|
65970
|
+
return removeNerdFont(item);
|
|
65971
|
+
}
|
|
65972
|
+
function formatCount(count, format, icon) {
|
|
65973
|
+
switch (format) {
|
|
65974
|
+
case "icon-space-number":
|
|
65975
|
+
return `${icon} ${count}`;
|
|
65976
|
+
case "text-and-number":
|
|
65977
|
+
return `Compactions: ${count}`;
|
|
65978
|
+
case "number":
|
|
65979
|
+
return String(count);
|
|
65980
|
+
}
|
|
65981
|
+
}
|
|
65982
|
+
|
|
65983
|
+
class CompactionCounterWidget {
|
|
65984
|
+
getDefaultColor() {
|
|
65985
|
+
return "yellow";
|
|
65986
|
+
}
|
|
65987
|
+
getDescription() {
|
|
65988
|
+
return "Count of context compaction events in the current session.";
|
|
65989
|
+
}
|
|
65990
|
+
getDisplayName() {
|
|
65991
|
+
return "Compaction Counter";
|
|
65992
|
+
}
|
|
65993
|
+
getCategory() {
|
|
65994
|
+
return "Context";
|
|
65995
|
+
}
|
|
65996
|
+
getEditorDisplay(item) {
|
|
65997
|
+
const modifiers = [getFormat2(item)];
|
|
65998
|
+
if (isNerdFontEnabled2(item)) {
|
|
65999
|
+
modifiers.push("nerd font");
|
|
66000
|
+
}
|
|
66001
|
+
if (isHideZeroEnabled(item)) {
|
|
66002
|
+
modifiers.push("hide zero");
|
|
66003
|
+
}
|
|
66004
|
+
return {
|
|
66005
|
+
displayText: "Compaction Counter",
|
|
66006
|
+
modifierText: `(${modifiers.join(", ")})`
|
|
66007
|
+
};
|
|
66008
|
+
}
|
|
66009
|
+
handleEditorAction(action, item) {
|
|
66010
|
+
if (action === CYCLE_FORMAT_ACTION2) {
|
|
66011
|
+
const currentFormat = getFormat2(item);
|
|
66012
|
+
const nextFormat = FORMATS2[(FORMATS2.indexOf(currentFormat) + 1) % FORMATS2.length] ?? DEFAULT_FORMAT2;
|
|
66013
|
+
return setFormat2(item, nextFormat);
|
|
66014
|
+
}
|
|
66015
|
+
if (action === TOGGLE_HIDE_ZERO_ACTION) {
|
|
66016
|
+
return toggleHideZero(item);
|
|
66017
|
+
}
|
|
66018
|
+
if (action === TOGGLE_NERD_FONT_ACTION2) {
|
|
66019
|
+
return toggleNerdFont2(item);
|
|
66020
|
+
}
|
|
66021
|
+
return null;
|
|
66022
|
+
}
|
|
66023
|
+
render(item, context, settings) {
|
|
66024
|
+
const format = getFormat2(item);
|
|
66025
|
+
const icon = isNerdFontEnabled2(item) ? COMPACTION_NERD_FONT_ICON : COMPACTION_ICON;
|
|
66026
|
+
if (context.isPreview) {
|
|
66027
|
+
return formatCount(2, format, icon);
|
|
66028
|
+
}
|
|
66029
|
+
const count = context.compactionData?.count ?? 0;
|
|
66030
|
+
if (count === 0 && isHideZeroEnabled(item))
|
|
66031
|
+
return null;
|
|
66032
|
+
return formatCount(count, format, icon);
|
|
66033
|
+
}
|
|
66034
|
+
getCustomKeybinds(item) {
|
|
66035
|
+
const keybinds = [
|
|
66036
|
+
{ key: "f", label: "(f)ormat", action: CYCLE_FORMAT_ACTION2 }
|
|
66037
|
+
];
|
|
66038
|
+
if (item === undefined || getFormat2(item) === DEFAULT_FORMAT2) {
|
|
66039
|
+
keybinds.push({ key: "n", label: "(n)erd font", action: TOGGLE_NERD_FONT_ACTION2 });
|
|
66040
|
+
}
|
|
66041
|
+
keybinds.push({ key: "h", label: "(h)ide when zero", action: TOGGLE_HIDE_ZERO_ACTION });
|
|
66042
|
+
return keybinds;
|
|
66043
|
+
}
|
|
66044
|
+
supportsRawValue() {
|
|
66045
|
+
return false;
|
|
66046
|
+
}
|
|
66047
|
+
supportsColors(item) {
|
|
66048
|
+
return true;
|
|
66049
|
+
}
|
|
66050
|
+
}
|
|
66051
|
+
var COMPACTION_ICON = "โป", COMPACTION_NERD_FONT_ICON = "๏ก", FORMATS2, DEFAULT_FORMAT2 = "icon-space-number", CYCLE_FORMAT_ACTION2 = "cycle-format", TOGGLE_HIDE_ZERO_ACTION = "toggle-hide-zero", TOGGLE_NERD_FONT_ACTION2 = "toggle-nerd-font", HIDE_ZERO_METADATA_KEY = "hideZero", NERD_FONT_METADATA_KEY2 = "nerdFont";
|
|
66052
|
+
var init_CompactionCounter = __esm(() => {
|
|
66053
|
+
FORMATS2 = ["icon-space-number", "text-and-number", "number"];
|
|
66054
|
+
});
|
|
66055
|
+
|
|
65896
66056
|
// src/widgets/index.ts
|
|
65897
66057
|
var init_widgets = __esm(async () => {
|
|
65898
66058
|
init_GitBranch();
|
|
@@ -65926,6 +66086,7 @@ var init_widgets = __esm(async () => {
|
|
|
65926
66086
|
init_FreeMemory();
|
|
65927
66087
|
init_SessionName();
|
|
65928
66088
|
init_VimMode();
|
|
66089
|
+
init_CompactionCounter();
|
|
65929
66090
|
await __promiseAll([
|
|
65930
66091
|
init_TokensInput(),
|
|
65931
66092
|
init_TokensOutput(),
|
|
@@ -66021,7 +66182,8 @@ var init_widget_manifest = __esm(async () => {
|
|
|
66021
66182
|
{ type: "worktree-mode", create: () => new GitWorktreeModeWidget },
|
|
66022
66183
|
{ type: "worktree-name", create: () => new GitWorktreeNameWidget },
|
|
66023
66184
|
{ type: "worktree-branch", create: () => new GitWorktreeBranchWidget },
|
|
66024
|
-
{ type: "worktree-original-branch", create: () => new GitWorktreeOriginalBranchWidget }
|
|
66185
|
+
{ type: "worktree-original-branch", create: () => new GitWorktreeOriginalBranchWidget },
|
|
66186
|
+
{ type: "compaction-counter", create: () => new CompactionCounterWidget }
|
|
66025
66187
|
];
|
|
66026
66188
|
LAYOUT_WIDGET_MANIFEST = [
|
|
66027
66189
|
{
|
|
@@ -72668,6 +72830,7 @@ var StatusJSONSchema = exports_external.looseObject({
|
|
|
72668
72830
|
}).optional(),
|
|
72669
72831
|
version: exports_external.string().optional(),
|
|
72670
72832
|
output_style: exports_external.object({ name: exports_external.string().optional() }).optional(),
|
|
72833
|
+
effort: exports_external.object({ level: exports_external.string().nullable().optional() }).nullable().optional(),
|
|
72671
72834
|
cost: exports_external.object({
|
|
72672
72835
|
total_cost_usd: CoercedNumberSchema.optional(),
|
|
72673
72836
|
total_duration_ms: CoercedNumberSchema.optional(),
|
|
@@ -72708,6 +72871,115 @@ var StatusJSONSchema = exports_external.looseObject({
|
|
|
72708
72871
|
// src/ccstatusline.ts
|
|
72709
72872
|
init_ansi();
|
|
72710
72873
|
init_colors();
|
|
72874
|
+
|
|
72875
|
+
// src/utils/compaction.ts
|
|
72876
|
+
init_zod();
|
|
72877
|
+
import * as crypto from "crypto";
|
|
72878
|
+
import * as fs13 from "fs";
|
|
72879
|
+
import * as os13 from "os";
|
|
72880
|
+
import * as path10 from "path";
|
|
72881
|
+
var DEFAULT_DROP_THRESHOLD = 2;
|
|
72882
|
+
var FRESH_PREV_CTX_PCT = -1;
|
|
72883
|
+
var MAX_CACHE_FILE_BYTES = 4096;
|
|
72884
|
+
var SESSION_ID_HASH_HEX_LEN = 32;
|
|
72885
|
+
var FRESH = { count: 0, prevCtxPct: FRESH_PREV_CTX_PCT };
|
|
72886
|
+
var CompactionStateSchema = exports_external.object({
|
|
72887
|
+
count: exports_external.number().int().nonnegative().default(0),
|
|
72888
|
+
prevCtxPct: exports_external.number().default(FRESH_PREV_CTX_PCT),
|
|
72889
|
+
prevWindowSize: exports_external.number().positive().nullable().optional()
|
|
72890
|
+
});
|
|
72891
|
+
function normalizeWindowSize(value) {
|
|
72892
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
72893
|
+
return null;
|
|
72894
|
+
}
|
|
72895
|
+
return value;
|
|
72896
|
+
}
|
|
72897
|
+
function normalizeOptions(options) {
|
|
72898
|
+
if (typeof options === "number") {
|
|
72899
|
+
return { dropThreshold: options, windowSize: null };
|
|
72900
|
+
}
|
|
72901
|
+
const dropThreshold = typeof options.dropThreshold === "number" && Number.isFinite(options.dropThreshold) ? options.dropThreshold : DEFAULT_DROP_THRESHOLD;
|
|
72902
|
+
return { dropThreshold, windowSize: options.windowSize ?? null };
|
|
72903
|
+
}
|
|
72904
|
+
function detectCompaction(currentCtxPct, state, options = DEFAULT_DROP_THRESHOLD) {
|
|
72905
|
+
if (!Number.isFinite(currentCtxPct) || currentCtxPct < 0) {
|
|
72906
|
+
return state;
|
|
72907
|
+
}
|
|
72908
|
+
const { dropThreshold, windowSize } = normalizeOptions(options);
|
|
72909
|
+
const currentWindowSize = normalizeWindowSize(windowSize);
|
|
72910
|
+
const prevWindowSize = normalizeWindowSize(state.prevWindowSize);
|
|
72911
|
+
let { count } = state;
|
|
72912
|
+
const { prevCtxPct } = state;
|
|
72913
|
+
const hasKnownWindowChange = currentWindowSize !== null && prevWindowSize !== null && currentWindowSize !== prevWindowSize;
|
|
72914
|
+
const isLearningWindowSize = currentWindowSize !== null && prevWindowSize === null && prevCtxPct >= 0;
|
|
72915
|
+
if (!hasKnownWindowChange && !isLearningWindowSize && prevCtxPct >= 0 && currentCtxPct < prevCtxPct - dropThreshold) {
|
|
72916
|
+
count += 1;
|
|
72917
|
+
}
|
|
72918
|
+
return {
|
|
72919
|
+
count,
|
|
72920
|
+
prevCtxPct: currentCtxPct,
|
|
72921
|
+
...currentWindowSize !== null ? { prevWindowSize: currentWindowSize } : {}
|
|
72922
|
+
};
|
|
72923
|
+
}
|
|
72924
|
+
function getCacheDir2() {
|
|
72925
|
+
return path10.join(os13.homedir(), ".cache", "ccstatusline", "compaction");
|
|
72926
|
+
}
|
|
72927
|
+
function sanitizeSessionId(sessionId) {
|
|
72928
|
+
const sanitized = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
72929
|
+
if (!sanitized || sanitized !== sessionId) {
|
|
72930
|
+
return crypto.createHash("sha256").update(sessionId).digest("hex").slice(0, SESSION_ID_HASH_HEX_LEN);
|
|
72931
|
+
}
|
|
72932
|
+
return sanitized;
|
|
72933
|
+
}
|
|
72934
|
+
function getStatePath(sessionId) {
|
|
72935
|
+
return path10.join(getCacheDir2(), `compaction-${sanitizeSessionId(sessionId)}.json`);
|
|
72936
|
+
}
|
|
72937
|
+
function loadCompactionState(sessionId) {
|
|
72938
|
+
const statePath = getStatePath(sessionId);
|
|
72939
|
+
let fd = null;
|
|
72940
|
+
try {
|
|
72941
|
+
fd = fs13.openSync(statePath, fs13.constants.O_RDONLY | fs13.constants.O_NOFOLLOW);
|
|
72942
|
+
const stats = fs13.fstatSync(fd);
|
|
72943
|
+
if (!stats.isFile() || stats.size > MAX_CACHE_FILE_BYTES) {
|
|
72944
|
+
return FRESH;
|
|
72945
|
+
}
|
|
72946
|
+
const raw = JSON.parse(fs13.readFileSync(fd, "utf-8"));
|
|
72947
|
+
const result2 = CompactionStateSchema.safeParse(raw);
|
|
72948
|
+
return result2.success ? result2.data : FRESH;
|
|
72949
|
+
} catch {
|
|
72950
|
+
return FRESH;
|
|
72951
|
+
} finally {
|
|
72952
|
+
if (fd !== null) {
|
|
72953
|
+
try {
|
|
72954
|
+
fs13.closeSync(fd);
|
|
72955
|
+
} catch {}
|
|
72956
|
+
}
|
|
72957
|
+
}
|
|
72958
|
+
}
|
|
72959
|
+
function saveCompactionState(sessionId, state) {
|
|
72960
|
+
let tmpPath = null;
|
|
72961
|
+
try {
|
|
72962
|
+
const dir = getCacheDir2();
|
|
72963
|
+
if (!fs13.existsSync(dir)) {
|
|
72964
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
72965
|
+
}
|
|
72966
|
+
const targetPath = getStatePath(sessionId);
|
|
72967
|
+
tmpPath = `${targetPath}.tmp.${process.pid}.${crypto.randomBytes(4).toString("hex")}`;
|
|
72968
|
+
fs13.writeFileSync(tmpPath, JSON.stringify(state) + `
|
|
72969
|
+
`);
|
|
72970
|
+
fs13.renameSync(tmpPath, targetPath);
|
|
72971
|
+
tmpPath = null;
|
|
72972
|
+
} catch {
|
|
72973
|
+
if (tmpPath !== null) {
|
|
72974
|
+
try {
|
|
72975
|
+
fs13.unlinkSync(tmpPath);
|
|
72976
|
+
} catch {}
|
|
72977
|
+
}
|
|
72978
|
+
}
|
|
72979
|
+
}
|
|
72980
|
+
|
|
72981
|
+
// src/ccstatusline.ts
|
|
72982
|
+
init_context_percentage();
|
|
72711
72983
|
await __promiseAll([
|
|
72712
72984
|
init_config(),
|
|
72713
72985
|
init_jsonl()
|
|
@@ -72715,23 +72987,23 @@ await __promiseAll([
|
|
|
72715
72987
|
await init_renderer2();
|
|
72716
72988
|
|
|
72717
72989
|
// src/utils/skills.ts
|
|
72718
|
-
import * as
|
|
72719
|
-
import * as
|
|
72720
|
-
import * as
|
|
72990
|
+
import * as fs14 from "fs";
|
|
72991
|
+
import * as os14 from "os";
|
|
72992
|
+
import * as path11 from "path";
|
|
72721
72993
|
var EMPTY = { totalInvocations: 0, uniqueSkills: [], lastSkill: null };
|
|
72722
72994
|
function getSkillsDir() {
|
|
72723
|
-
return
|
|
72995
|
+
return path11.join(os14.homedir(), ".cache", "ccstatusline", "skills");
|
|
72724
72996
|
}
|
|
72725
72997
|
function getSkillsFilePath(sessionId) {
|
|
72726
|
-
return
|
|
72998
|
+
return path11.join(getSkillsDir(), `skills-${sessionId}.jsonl`);
|
|
72727
72999
|
}
|
|
72728
73000
|
function getSkillsMetrics(sessionId) {
|
|
72729
73001
|
const filePath = getSkillsFilePath(sessionId);
|
|
72730
|
-
if (!
|
|
73002
|
+
if (!fs14.existsSync(filePath)) {
|
|
72731
73003
|
return EMPTY;
|
|
72732
73004
|
}
|
|
72733
73005
|
try {
|
|
72734
|
-
const invocations =
|
|
73006
|
+
const invocations = fs14.readFileSync(filePath, "utf-8").trim().split(`
|
|
72735
73007
|
`).filter((line) => line.trim()).map((line) => {
|
|
72736
73008
|
try {
|
|
72737
73009
|
return JSON.parse(line);
|
|
@@ -72880,6 +73152,20 @@ async function renderMultipleLines(data) {
|
|
|
72880
73152
|
if (data.session_id) {
|
|
72881
73153
|
skillsMetrics = getSkillsMetrics(data.session_id);
|
|
72882
73154
|
}
|
|
73155
|
+
let compactionCount = 0;
|
|
73156
|
+
const hasCompactionWidget = lines.some((line) => line.some((item) => item.type === "compaction-counter"));
|
|
73157
|
+
if (hasCompactionWidget && data.session_id) {
|
|
73158
|
+
const prevState = loadCompactionState(data.session_id);
|
|
73159
|
+
compactionCount = prevState.count;
|
|
73160
|
+
const contextPercentageMetrics = calculateContextPercentageMetrics({ data, tokenMetrics });
|
|
73161
|
+
if (contextPercentageMetrics !== null) {
|
|
73162
|
+
const newState = detectCompaction(contextPercentageMetrics.usedPercentage, prevState, { windowSize: contextPercentageMetrics.windowSize });
|
|
73163
|
+
if (newState.count !== prevState.count || newState.prevCtxPct !== prevState.prevCtxPct || newState.prevWindowSize !== prevState.prevWindowSize) {
|
|
73164
|
+
saveCompactionState(data.session_id, newState);
|
|
73165
|
+
}
|
|
73166
|
+
compactionCount = newState.count;
|
|
73167
|
+
}
|
|
73168
|
+
}
|
|
72883
73169
|
const context = {
|
|
72884
73170
|
data,
|
|
72885
73171
|
tokenMetrics,
|
|
@@ -72888,6 +73174,7 @@ async function renderMultipleLines(data) {
|
|
|
72888
73174
|
usageData,
|
|
72889
73175
|
sessionDuration,
|
|
72890
73176
|
skillsMetrics,
|
|
73177
|
+
compactionData: hasCompactionWidget ? { count: compactionCount } : null,
|
|
72891
73178
|
isPreview: false,
|
|
72892
73179
|
minimalist: settings.minimalistMode
|
|
72893
73180
|
};
|
|
@@ -72974,16 +73261,16 @@ async function handleHook() {
|
|
|
72974
73261
|
return;
|
|
72975
73262
|
}
|
|
72976
73263
|
const filePath = getSkillsFilePath(sessionId);
|
|
72977
|
-
const
|
|
72978
|
-
const
|
|
72979
|
-
|
|
73264
|
+
const fs15 = await import("fs");
|
|
73265
|
+
const path12 = await import("path");
|
|
73266
|
+
fs15.mkdirSync(path12.dirname(filePath), { recursive: true });
|
|
72980
73267
|
const entry = JSON.stringify({
|
|
72981
73268
|
timestamp: new Date().toISOString(),
|
|
72982
73269
|
session_id: sessionId,
|
|
72983
73270
|
skill: skillName,
|
|
72984
73271
|
source: data.hook_event_name
|
|
72985
73272
|
});
|
|
72986
|
-
|
|
73273
|
+
fs15.appendFileSync(filePath, entry + `
|
|
72987
73274
|
`);
|
|
72988
73275
|
} catch {}
|
|
72989
73276
|
console.log("{}");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccstatusline",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.11",
|
|
4
4
|
"description": "A customizable status line formatter for Claude Code CLI",
|
|
5
5
|
"module": "src/ccstatusline.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"strip-ansi": "^7.1.0",
|
|
46
46
|
"tinyglobby": "^0.2.14",
|
|
47
47
|
"typedoc": "^0.28.12",
|
|
48
|
-
"typescript": "^
|
|
48
|
+
"typescript": "^6.0.2",
|
|
49
49
|
"typescript-eslint": "^8.39.1",
|
|
50
50
|
"vitest": "^4.0.18",
|
|
51
51
|
"zod": "^4.0.17"
|