claude-contextline 2.0.0 → 2.0.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/dist/index.js +52 -167
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -43,9 +43,8 @@ function getEnvironmentInfo(hookData) {
|
|
|
43
43
|
return {
|
|
44
44
|
directory: getDirectoryName(cwd),
|
|
45
45
|
gitBranch: getGitBranch(cwd),
|
|
46
|
-
gitDirty: isGitDirty(cwd),
|
|
47
46
|
model: getModelName(hookData),
|
|
48
|
-
|
|
47
|
+
usedPercentage: getUsedPercentage(hookData)
|
|
49
48
|
};
|
|
50
49
|
}
|
|
51
50
|
function getDirectoryName(cwd) {
|
|
@@ -57,198 +56,84 @@ function getGitBranch(cwd) {
|
|
|
57
56
|
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
58
57
|
cwd,
|
|
59
58
|
encoding: "utf8",
|
|
60
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
59
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
60
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
|
|
61
61
|
}).trim();
|
|
62
62
|
if (branch === "HEAD") {
|
|
63
|
-
return
|
|
64
|
-
cwd,
|
|
65
|
-
encoding: "utf8",
|
|
66
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
67
|
-
}).trim();
|
|
63
|
+
return null;
|
|
68
64
|
}
|
|
69
65
|
return branch;
|
|
70
66
|
} catch {
|
|
71
67
|
return null;
|
|
72
68
|
}
|
|
73
69
|
}
|
|
74
|
-
function isGitDirty(cwd) {
|
|
75
|
-
try {
|
|
76
|
-
const status = execSync("git status --porcelain", {
|
|
77
|
-
cwd,
|
|
78
|
-
encoding: "utf8",
|
|
79
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
80
|
-
});
|
|
81
|
-
return status.trim().length > 0;
|
|
82
|
-
} catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
70
|
function getModelName(hookData) {
|
|
87
71
|
const displayName = hookData.model?.display_name || "Claude";
|
|
88
72
|
return displayName.replace(/^Claude\s+/, "");
|
|
89
73
|
}
|
|
90
|
-
function
|
|
74
|
+
function getUsedPercentage(hookData) {
|
|
91
75
|
const ctx = hookData.context_window;
|
|
92
|
-
if (!ctx
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const usage = ctx.current_usage;
|
|
96
|
-
const totalTokens = (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
|
|
97
|
-
return Math.round(totalTokens / ctx.context_window_size * 100);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/utils/constants.ts
|
|
101
|
-
var SYMBOLS = {
|
|
102
|
-
arrow: "\uE0B0",
|
|
103
|
-
// Powerline right arrow
|
|
104
|
-
branch: "\uE0A0",
|
|
105
|
-
// Git branch icon
|
|
106
|
-
model: "\u2731",
|
|
107
|
-
// Heavy asterisk ✱
|
|
108
|
-
context: "\u25EB",
|
|
109
|
-
// White square with vertical bisecting line ◫
|
|
110
|
-
dirty: "\u25CF"
|
|
111
|
-
// Dirty indicator
|
|
112
|
-
};
|
|
113
|
-
var TEXT_SYMBOLS = {
|
|
114
|
-
arrow: "",
|
|
115
|
-
branch: "",
|
|
116
|
-
model: "",
|
|
117
|
-
context: "",
|
|
118
|
-
dirty: "*"
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// src/themes/index.ts
|
|
122
|
-
var darkTheme = {
|
|
123
|
-
directory: { bg: "#8b4513", fg: "#ffffff" },
|
|
124
|
-
// Brown, white
|
|
125
|
-
git: { bg: "#404040", fg: "#ffffff" },
|
|
126
|
-
// Dark gray, white
|
|
127
|
-
model: { bg: "#2d2d2d", fg: "#ffffff" },
|
|
128
|
-
// Very dark gray, white
|
|
129
|
-
context: { bg: "#2a2a2a", fg: "#87ceeb" },
|
|
130
|
-
// Nearly black, sky blue
|
|
131
|
-
warning: { bg: "#d75f00", fg: "#ffffff" },
|
|
132
|
-
// Orange, white (80%+)
|
|
133
|
-
critical: { bg: "#af0000", fg: "#ffffff" }
|
|
134
|
-
// Red, white (100%+)
|
|
135
|
-
};
|
|
136
|
-
function hexToAnsi256(hex) {
|
|
137
|
-
const r = parseInt(hex.slice(1, 3), 16);
|
|
138
|
-
const g = parseInt(hex.slice(3, 5), 16);
|
|
139
|
-
const b = parseInt(hex.slice(5, 7), 16);
|
|
140
|
-
if (r === g && g === b) {
|
|
141
|
-
if (r < 8) return 16;
|
|
142
|
-
if (r > 248) return 231;
|
|
143
|
-
return Math.round((r - 8) / 247 * 24) + 232;
|
|
76
|
+
if (!ctx) return null;
|
|
77
|
+
if (ctx.used_percentage != null) {
|
|
78
|
+
return Math.floor(ctx.used_percentage);
|
|
144
79
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
var ansi = {
|
|
151
|
-
fg: (hex) => `\x1B[38;5;${hexToAnsi256(hex)}m`,
|
|
152
|
-
bg: (hex) => `\x1B[48;5;${hexToAnsi256(hex)}m`,
|
|
153
|
-
reset: "\x1B[0m"
|
|
154
|
-
};
|
|
155
|
-
function getContextColors(percent) {
|
|
156
|
-
if (percent >= 100) {
|
|
157
|
-
return darkTheme.critical;
|
|
158
|
-
} else if (percent >= 80) {
|
|
159
|
-
return darkTheme.warning;
|
|
80
|
+
if (ctx.current_usage && ctx.context_window_size) {
|
|
81
|
+
const usage = ctx.current_usage;
|
|
82
|
+
const totalTokens = (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
|
|
83
|
+
return Math.floor(totalTokens / ctx.context_window_size * 100);
|
|
160
84
|
}
|
|
161
|
-
return
|
|
85
|
+
return null;
|
|
162
86
|
}
|
|
163
87
|
|
|
164
88
|
// src/renderer.ts
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
89
|
+
var ESC = "\x1B";
|
|
90
|
+
var RESET = `${ESC}[0m`;
|
|
91
|
+
var BLUE = `${ESC}[38;5;69m`;
|
|
92
|
+
var RED = `${ESC}[38;5;196m`;
|
|
93
|
+
var GRAY = `${ESC}[38;5;243m`;
|
|
94
|
+
var BAR_WIDTH = 10;
|
|
95
|
+
var FILLED_CHAR = "\u2588";
|
|
96
|
+
var EMPTY_CHAR = "\u2591";
|
|
97
|
+
function render(envInfo) {
|
|
98
|
+
let out = "";
|
|
99
|
+
let modelCol = 0;
|
|
100
|
+
if (envInfo.usedPercentage != null) {
|
|
101
|
+
const pct = Math.max(0, envInfo.usedPercentage);
|
|
102
|
+
const pctStr = String(pct);
|
|
103
|
+
const nFilled = Math.min(
|
|
104
|
+
Math.floor(pct * BAR_WIDTH / 100),
|
|
105
|
+
BAR_WIDTH
|
|
106
|
+
);
|
|
107
|
+
const nEmpty = BAR_WIDTH - nFilled;
|
|
108
|
+
const filled = FILLED_CHAR.repeat(nFilled);
|
|
109
|
+
const empty = EMPTY_CHAR.repeat(nEmpty);
|
|
110
|
+
out += `${BLUE}[${filled}${GRAY}${empty}${BLUE}] ${pctStr}%`;
|
|
111
|
+
modelCol = 16 + pctStr.length;
|
|
184
112
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
*/
|
|
188
|
-
render(envInfo) {
|
|
189
|
-
const segments = this.buildSegments(envInfo);
|
|
190
|
-
if (segments.length === 0) {
|
|
191
|
-
return "";
|
|
192
|
-
}
|
|
193
|
-
return this.renderPowerline(segments);
|
|
113
|
+
if (out.length > 0) {
|
|
114
|
+
out += " ";
|
|
194
115
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (envInfo.gitBranch) {
|
|
205
|
-
const dirty = envInfo.gitDirty ? ` ${this.symbols.dirty}` : "";
|
|
206
|
-
segments.push({
|
|
207
|
-
text: ` ${this.symbols.branch} ${envInfo.gitBranch}${dirty} `,
|
|
208
|
-
colors: darkTheme.git
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
segments.push({
|
|
212
|
-
text: ` ${this.symbols.model} ${envInfo.model} `,
|
|
213
|
-
colors: darkTheme.model
|
|
214
|
-
});
|
|
215
|
-
const contextColors = getContextColors(envInfo.contextPercent);
|
|
216
|
-
segments.push({
|
|
217
|
-
text: ` ${this.symbols.context} ${envInfo.contextPercent}% `,
|
|
218
|
-
colors: contextColors
|
|
219
|
-
});
|
|
220
|
-
return segments;
|
|
116
|
+
out += `${RED}${envInfo.model}`;
|
|
117
|
+
out += "\n";
|
|
118
|
+
if (envInfo.gitBranch) {
|
|
119
|
+
const branchText = `(${envInfo.gitBranch})`;
|
|
120
|
+
out += `${RED}${branchText}`;
|
|
121
|
+
const gap = Math.max(2, modelCol - branchText.length);
|
|
122
|
+
out += " ".repeat(gap);
|
|
123
|
+
} else {
|
|
124
|
+
out += " ".repeat(modelCol);
|
|
221
125
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
let output = "";
|
|
227
|
-
for (let i = 0; i < segments.length; i++) {
|
|
228
|
-
const seg = segments[i];
|
|
229
|
-
const nextColors = i < segments.length - 1 ? segments[i + 1].colors : null;
|
|
230
|
-
output += ansi.bg(seg.colors.bg) + ansi.fg(seg.colors.fg) + seg.text;
|
|
231
|
-
output += ansi.reset;
|
|
232
|
-
if (this.noArrows) {
|
|
233
|
-
} else if (nextColors) {
|
|
234
|
-
output += ansi.fg(seg.colors.bg) + ansi.bg(nextColors.bg) + this.symbols.arrow;
|
|
235
|
-
} else {
|
|
236
|
-
output += ansi.fg(seg.colors.bg) + this.symbols.arrow;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
output += ansi.reset;
|
|
240
|
-
return output;
|
|
241
|
-
}
|
|
242
|
-
};
|
|
126
|
+
out += `${BLUE}${envInfo.directory}`;
|
|
127
|
+
out += RESET;
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
243
130
|
|
|
244
131
|
// src/index.ts
|
|
245
132
|
async function main() {
|
|
246
133
|
try {
|
|
247
|
-
const noArrows = process.argv.includes("--no-arrows");
|
|
248
134
|
const hookData = await readHookData();
|
|
249
135
|
const envInfo = getEnvironmentInfo(hookData);
|
|
250
|
-
const
|
|
251
|
-
const output = renderer.render(envInfo);
|
|
136
|
+
const output = render(envInfo);
|
|
252
137
|
process.stdout.write(output);
|
|
253
138
|
} catch {
|
|
254
139
|
process.exit(0);
|