@narumitw/pi-statusline 0.49.0 → 0.49.3
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 -5
- package/dist/chunks/chunk-6BDIM4OT.ts +776 -0
- package/dist/chunks/chunk-6BDIM4OT.ts.map +7 -0
- package/dist/chunks/commands-FUT5RTFK.ts +774 -0
- package/dist/chunks/commands-FUT5RTFK.ts.map +7 -0
- package/dist/statusline.ts +1144 -0
- package/dist/statusline.ts.map +7 -0
- package/package.json +10 -6
- package/src/index.ts +1 -1
- package/src/ansi.ts +0 -26
- package/src/commands.ts +0 -929
- package/src/directory.ts +0 -54
- package/src/extension-status.ts +0 -323
- package/src/git-status.ts +0 -148
- package/src/information-profiles.ts +0 -40
- package/src/powerline.ts +0 -170
- package/src/presets/candy.ts +0 -9
- package/src/presets/create-ramp.ts +0 -41
- package/src/presets/custom.ts +0 -11
- package/src/presets/forest.ts +0 -9
- package/src/presets/index.ts +0 -55
- package/src/presets/mono.ts +0 -9
- package/src/presets/neon.ts +0 -9
- package/src/presets/ocean.ts +0 -9
- package/src/presets/sunset.ts +0 -9
- package/src/presets/tokyo-night.ts +0 -13
- package/src/presets/types.ts +0 -12
- package/src/render.ts +0 -357
- package/src/settings.ts +0 -702
- package/src/statusline.ts +0 -353
- package/src/terminal.ts +0 -60
- package/src/types.ts +0 -88
- package/src/usage.ts +0 -53
package/src/statusline.ts
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import {
|
|
3
|
-
type ExtensionAPI,
|
|
4
|
-
type ExtensionContext,
|
|
5
|
-
getAgentDir,
|
|
6
|
-
} from "@earendil-works/pi-coding-agent";
|
|
7
|
-
import { registerStatuslineCommand } from "./commands.js";
|
|
8
|
-
import {
|
|
9
|
-
buildExtensionStatusIconAliases,
|
|
10
|
-
type ExtensionStatusIconAliasMap,
|
|
11
|
-
findDuplicateExtensions,
|
|
12
|
-
readInstalledExtensionPackages,
|
|
13
|
-
} from "./extension-status.js";
|
|
14
|
-
import { type GitStatusSummary, gitStatusSummaryEqual, readGitStatus } from "./git-status.js";
|
|
15
|
-
import { type RuntimeState, renderExtensionStatusline, renderStatusline } from "./render.js";
|
|
16
|
-
import {
|
|
17
|
-
consumeStatuslineSettingsNotice,
|
|
18
|
-
type LoadedStatuslineSettings,
|
|
19
|
-
loadStatuslineSettings,
|
|
20
|
-
loadStatuslineSettingsForAgent,
|
|
21
|
-
settingsFilePath,
|
|
22
|
-
} from "./settings.js";
|
|
23
|
-
import type { PalettePreset } from "./types.js";
|
|
24
|
-
|
|
25
|
-
const STATUSLINE_KEY = "statusline";
|
|
26
|
-
const GIT_STATUS_REFRESH_INTERVAL_MS = 30_000;
|
|
27
|
-
const GIT_STATUS_EVENT_DEBOUNCE_MS = 250;
|
|
28
|
-
const EMPTY_EXTENSION_STATUS_ICON_ALIASES: ExtensionStatusIconAliasMap = new Map();
|
|
29
|
-
|
|
30
|
-
export default function statusline(pi: ExtensionAPI) {
|
|
31
|
-
let loaded: LoadedStatuslineSettings | undefined;
|
|
32
|
-
let previewPalettePreset: PalettePreset | undefined;
|
|
33
|
-
let activeSessionManager: ExtensionContext["sessionManager"] | undefined;
|
|
34
|
-
const runtime: RuntimeState = {
|
|
35
|
-
turnCount: 0,
|
|
36
|
-
activeTools: new Map(),
|
|
37
|
-
isStreaming: false,
|
|
38
|
-
thinkingLevel: "off",
|
|
39
|
-
duplicateExtensions: [],
|
|
40
|
-
extensionStatusIconAliases: EMPTY_EXTENSION_STATUS_ICON_ALIASES,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
let sessionGeneration = 0;
|
|
44
|
-
let menuController = new AbortController();
|
|
45
|
-
let gitStatusRequestId = 0;
|
|
46
|
-
let activeGitStatusTarget: { cwd: string; generation: number } | undefined;
|
|
47
|
-
let gitStatusRefreshInFlight = false;
|
|
48
|
-
let gitStatusAbortController: AbortController | undefined;
|
|
49
|
-
let gitStatusDebounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
50
|
-
let pendingGitStatusRefresh: { cwd: string; generation: number; requestId: number } | undefined;
|
|
51
|
-
|
|
52
|
-
const refresh = () => runtime.requestRender?.();
|
|
53
|
-
const ownsRuntime = (ctx: ExtensionContext) => ctx.sessionManager === activeSessionManager;
|
|
54
|
-
|
|
55
|
-
const setGitStatus = (summary: GitStatusSummary | undefined) => {
|
|
56
|
-
if (gitStatusSummaryEqual(runtime.gitStatus, summary)) return;
|
|
57
|
-
runtime.gitStatus = summary;
|
|
58
|
-
refresh();
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const clearGitStatusDebounce = () => {
|
|
62
|
-
if (!gitStatusDebounceTimer) return;
|
|
63
|
-
clearTimeout(gitStatusDebounceTimer);
|
|
64
|
-
gitStatusDebounceTimer = undefined;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const abortGitStatusRefresh = (reason: string) => {
|
|
68
|
-
gitStatusAbortController?.abort(new DOMException(reason, "AbortError"));
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const isActiveGitStatusTarget = (cwd: string, generation: number) =>
|
|
72
|
-
activeGitStatusTarget?.cwd === cwd &&
|
|
73
|
-
activeGitStatusTarget.generation === generation &&
|
|
74
|
-
generation === sessionGeneration;
|
|
75
|
-
|
|
76
|
-
const isCurrentGitStatusRequest = (cwd: string, generation: number, requestId: number) =>
|
|
77
|
-
isActiveGitStatusTarget(cwd, generation) && requestId === gitStatusRequestId;
|
|
78
|
-
|
|
79
|
-
const runGitStatusRefresh = (cwd: string, generation: number, requestId: number) => {
|
|
80
|
-
if (!isCurrentGitStatusRequest(cwd, generation, requestId)) return;
|
|
81
|
-
if (gitStatusRefreshInFlight) {
|
|
82
|
-
pendingGitStatusRefresh = { cwd, generation, requestId };
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
gitStatusRefreshInFlight = true;
|
|
87
|
-
const abortController = new AbortController();
|
|
88
|
-
gitStatusAbortController = abortController;
|
|
89
|
-
void (async () => {
|
|
90
|
-
try {
|
|
91
|
-
const summary = await readGitStatus(pi, cwd, abortController.signal);
|
|
92
|
-
if (isCurrentGitStatusRequest(cwd, generation, requestId)) setGitStatus(summary);
|
|
93
|
-
} catch {
|
|
94
|
-
if (isCurrentGitStatusRequest(cwd, generation, requestId)) setGitStatus(undefined);
|
|
95
|
-
} finally {
|
|
96
|
-
gitStatusRefreshInFlight = false;
|
|
97
|
-
if (gitStatusAbortController === abortController) gitStatusAbortController = undefined;
|
|
98
|
-
const pending = pendingGitStatusRefresh;
|
|
99
|
-
pendingGitStatusRefresh = undefined;
|
|
100
|
-
if (pending) runGitStatusRefresh(pending.cwd, pending.generation, pending.requestId);
|
|
101
|
-
}
|
|
102
|
-
})();
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const refreshGitStatus = (cwd: string, generation = sessionGeneration) => {
|
|
106
|
-
if (!isActiveGitStatusTarget(cwd, generation)) return;
|
|
107
|
-
runGitStatusRefresh(cwd, generation, ++gitStatusRequestId);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const scheduleGitStatusRefresh = (cwd: string, generation = sessionGeneration) => {
|
|
111
|
-
if (!isActiveGitStatusTarget(cwd, generation)) return;
|
|
112
|
-
const requestId = ++gitStatusRequestId;
|
|
113
|
-
clearGitStatusDebounce();
|
|
114
|
-
gitStatusDebounceTimer = setTimeout(() => {
|
|
115
|
-
gitStatusDebounceTimer = undefined;
|
|
116
|
-
runGitStatusRefresh(cwd, generation, requestId);
|
|
117
|
-
}, GIT_STATUS_EVENT_DEBOUNCE_MS);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const scheduleGitStatusRefreshForContext = (ctx: ExtensionContext) => {
|
|
121
|
-
if (!activeGitStatusTarget || activeGitStatusTarget.cwd !== ctx.cwd) return;
|
|
122
|
-
scheduleGitStatusRefresh(activeGitStatusTarget.cwd, activeGitStatusTarget.generation);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const installFooter = (ctx: ExtensionContext) => {
|
|
126
|
-
const generation = ++sessionGeneration;
|
|
127
|
-
menuController.abort(new DOMException("Statusline session context replaced", "AbortError"));
|
|
128
|
-
menuController = new AbortController();
|
|
129
|
-
const cwd = ctx.cwd;
|
|
130
|
-
activeSessionManager = ctx.sessionManager;
|
|
131
|
-
runtime.homeDir = homedir();
|
|
132
|
-
previewPalettePreset = undefined;
|
|
133
|
-
abortGitStatusRefresh("Statusline session context replaced");
|
|
134
|
-
clearGitStatusDebounce();
|
|
135
|
-
activeGitStatusTarget = ctx.mode === "tui" ? { cwd, generation } : undefined;
|
|
136
|
-
runtime.gitStatus = undefined;
|
|
137
|
-
runtime.duplicateExtensions = [];
|
|
138
|
-
runtime.extensionStatusIconAliases = EMPTY_EXTENSION_STATUS_ICON_ALIASES;
|
|
139
|
-
ctx.ui.setStatus(STATUSLINE_KEY, undefined);
|
|
140
|
-
if (!activeGitStatusTarget || !loaded) return;
|
|
141
|
-
const installedPackages = readInstalledExtensionPackages(cwd);
|
|
142
|
-
runtime.duplicateExtensions = findDuplicateExtensions(installedPackages);
|
|
143
|
-
runtime.extensionStatusIconAliases = buildExtensionStatusIconAliases(installedPackages);
|
|
144
|
-
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
145
|
-
runtime.requestRender = () => tui.requestRender();
|
|
146
|
-
|
|
147
|
-
const refreshFooterGitStatus = () => refreshGitStatus(cwd, generation);
|
|
148
|
-
const branchUnsubscribe = footerData.onBranchChange(() => {
|
|
149
|
-
runtime.gitStatus = undefined;
|
|
150
|
-
abortGitStatusRefresh("Statusline Git branch changed");
|
|
151
|
-
clearGitStatusDebounce();
|
|
152
|
-
refreshFooterGitStatus();
|
|
153
|
-
tui.requestRender();
|
|
154
|
-
});
|
|
155
|
-
const clock = setInterval(() => {
|
|
156
|
-
clearGitStatusDebounce();
|
|
157
|
-
refreshFooterGitStatus();
|
|
158
|
-
tui.requestRender();
|
|
159
|
-
}, GIT_STATUS_REFRESH_INTERVAL_MS);
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
dispose() {
|
|
163
|
-
branchUnsubscribe();
|
|
164
|
-
clearInterval(clock);
|
|
165
|
-
if (isActiveGitStatusTarget(cwd, generation)) {
|
|
166
|
-
activeGitStatusTarget = undefined;
|
|
167
|
-
abortGitStatusRefresh("Statusline footer disposed");
|
|
168
|
-
clearGitStatusDebounce();
|
|
169
|
-
pendingGitStatusRefresh = undefined;
|
|
170
|
-
runtime.gitStatus = undefined;
|
|
171
|
-
runtime.duplicateExtensions = [];
|
|
172
|
-
runtime.extensionStatusIconAliases = EMPTY_EXTENSION_STATUS_ICON_ALIASES;
|
|
173
|
-
runtime.requestRender = undefined;
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
invalidate() {},
|
|
177
|
-
render(width: number): string[] {
|
|
178
|
-
if (!loaded) return [];
|
|
179
|
-
const config = previewPalettePreset
|
|
180
|
-
? { ...loaded.config, palettePreset: previewPalettePreset }
|
|
181
|
-
: loaded.config;
|
|
182
|
-
const mainLine = renderStatusline(width, ctx, footerData, theme, config, runtime);
|
|
183
|
-
const lines = mainLine ? mainLine.split("\n") : [];
|
|
184
|
-
lines.push(
|
|
185
|
-
...renderExtensionStatusline(width, footerData, theme, config, runtime, mainLine),
|
|
186
|
-
);
|
|
187
|
-
return lines;
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
});
|
|
191
|
-
refreshGitStatus(cwd, generation);
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const agentDir = getAgentDir();
|
|
195
|
-
const configPath = settingsFilePath(agentDir);
|
|
196
|
-
registerStatuslineCommand(pi, {
|
|
197
|
-
settingsPath: configPath,
|
|
198
|
-
getLoaded: () => loaded ?? loadStatuslineSettings(configPath),
|
|
199
|
-
getMenuOwner: () => {
|
|
200
|
-
const generation = sessionGeneration;
|
|
201
|
-
return {
|
|
202
|
-
signal: menuController.signal,
|
|
203
|
-
isCurrent: () => generation === sessionGeneration && !menuController.signal.aborted,
|
|
204
|
-
};
|
|
205
|
-
},
|
|
206
|
-
apply(next, ctx) {
|
|
207
|
-
if (ctx.sessionManager !== activeSessionManager) return;
|
|
208
|
-
previewPalettePreset = undefined;
|
|
209
|
-
loaded = next;
|
|
210
|
-
refresh();
|
|
211
|
-
},
|
|
212
|
-
preview(palettePreset, ctx) {
|
|
213
|
-
if (ctx.sessionManager !== activeSessionManager) return;
|
|
214
|
-
previewPalettePreset = palettePreset;
|
|
215
|
-
refresh();
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
pi.on("session_start", (_event, ctx) => {
|
|
220
|
-
runtime.turnCount = 0;
|
|
221
|
-
runtime.activeTools.clear();
|
|
222
|
-
runtime.isStreaming = false;
|
|
223
|
-
loaded = loadStatuslineSettingsForAgent(agentDir);
|
|
224
|
-
const settingsNotice = consumeStatuslineSettingsNotice();
|
|
225
|
-
if (settingsNotice) ctx.ui.notify(settingsNotice, "warning");
|
|
226
|
-
if (loaded.diagnostics.length > 0) {
|
|
227
|
-
ctx.ui.notify(formatSettingsDiagnostics(loaded), "warning");
|
|
228
|
-
}
|
|
229
|
-
runtime.thinkingLevel = pi.getThinkingLevel();
|
|
230
|
-
installFooter(ctx);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
pi.on("session_tree", (_event, ctx) => {
|
|
234
|
-
installFooter(ctx);
|
|
235
|
-
refresh();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
pi.on("session_shutdown", (_event, ctx) => {
|
|
239
|
-
if (!ownsRuntime(ctx)) return;
|
|
240
|
-
sessionGeneration += 1;
|
|
241
|
-
menuController.abort(new DOMException("Statusline session shut down", "AbortError"));
|
|
242
|
-
activeSessionManager = undefined;
|
|
243
|
-
previewPalettePreset = undefined;
|
|
244
|
-
activeGitStatusTarget = undefined;
|
|
245
|
-
abortGitStatusRefresh("Statusline session shut down");
|
|
246
|
-
clearGitStatusDebounce();
|
|
247
|
-
pendingGitStatusRefresh = undefined;
|
|
248
|
-
runtime.gitStatus = undefined;
|
|
249
|
-
runtime.activeTools.clear();
|
|
250
|
-
runtime.isStreaming = false;
|
|
251
|
-
runtime.duplicateExtensions = [];
|
|
252
|
-
runtime.extensionStatusIconAliases = EMPTY_EXTENSION_STATUS_ICON_ALIASES;
|
|
253
|
-
ctx.ui.setFooter(undefined);
|
|
254
|
-
ctx.ui.setStatus(STATUSLINE_KEY, undefined);
|
|
255
|
-
runtime.requestRender = undefined;
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
pi.on("model_select", () => refresh());
|
|
259
|
-
|
|
260
|
-
pi.on("thinking_level_select", (event) => {
|
|
261
|
-
runtime.thinkingLevel = event.level;
|
|
262
|
-
refresh();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
pi.on("agent_start", (_event, ctx) => {
|
|
266
|
-
if (!ownsRuntime(ctx)) return;
|
|
267
|
-
runtime.isStreaming = true;
|
|
268
|
-
refresh();
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
pi.on("agent_end", (_event, ctx) => {
|
|
272
|
-
if (!ownsRuntime(ctx)) return;
|
|
273
|
-
runtime.activeTools.clear();
|
|
274
|
-
scheduleGitStatusRefreshForContext(ctx);
|
|
275
|
-
refresh();
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
pi.on("agent_settled", (_event, ctx) => {
|
|
279
|
-
if (!ownsRuntime(ctx)) return;
|
|
280
|
-
runtime.isStreaming = false;
|
|
281
|
-
runtime.activeTools.clear();
|
|
282
|
-
refresh();
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
pi.on("turn_start", (_event, ctx) => {
|
|
286
|
-
if (!ownsRuntime(ctx)) return;
|
|
287
|
-
runtime.turnCount += 1;
|
|
288
|
-
runtime.isStreaming = true;
|
|
289
|
-
refresh();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
pi.on("turn_end", (_event, ctx) => {
|
|
293
|
-
if (!ownsRuntime(ctx)) return;
|
|
294
|
-
scheduleGitStatusRefreshForContext(ctx);
|
|
295
|
-
refresh();
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
pi.on("tool_execution_start", (event, ctx) => {
|
|
299
|
-
if (!ownsRuntime(ctx)) return;
|
|
300
|
-
const currentCount = runtime.activeTools.get(event.toolName) ?? 0;
|
|
301
|
-
runtime.activeTools.set(event.toolName, currentCount + 1);
|
|
302
|
-
refresh();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
pi.on("tool_execution_end", (event, ctx) => {
|
|
306
|
-
if (!ownsRuntime(ctx)) return;
|
|
307
|
-
const currentCount = runtime.activeTools.get(event.toolName) ?? 0;
|
|
308
|
-
if (currentCount <= 1) runtime.activeTools.delete(event.toolName);
|
|
309
|
-
else runtime.activeTools.set(event.toolName, currentCount - 1);
|
|
310
|
-
|
|
311
|
-
scheduleGitStatusRefreshForContext(ctx);
|
|
312
|
-
refresh();
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function formatSettingsDiagnostics(loaded: LoadedStatuslineSettings): string {
|
|
317
|
-
const details = loaded.diagnostics.slice(0, 5).map((item) => item.message);
|
|
318
|
-
const remaining = loaded.diagnostics.length - details.length;
|
|
319
|
-
return [
|
|
320
|
-
`pi-statusline settings: ${details.join("; ")}`,
|
|
321
|
-
...(remaining > 0 ? [`+${remaining} more`] : []),
|
|
322
|
-
].join(" ");
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export {
|
|
326
|
-
buildExtensionStatusIconAliases,
|
|
327
|
-
type ExtensionStatusIconAliasMap,
|
|
328
|
-
extensionColor,
|
|
329
|
-
formatExtensionStatus,
|
|
330
|
-
npmPackageName,
|
|
331
|
-
simplifyExtensionStatusText,
|
|
332
|
-
splitExtensionStatusIcon,
|
|
333
|
-
stripExtensionStatusPrefix,
|
|
334
|
-
wrapExtensionStatusline,
|
|
335
|
-
} from "./extension-status.js";
|
|
336
|
-
export {
|
|
337
|
-
formatGitBranchText,
|
|
338
|
-
formatGitBranchValue,
|
|
339
|
-
formatGitStatusSummary,
|
|
340
|
-
type GitStatusSummary,
|
|
341
|
-
parseGitRoot,
|
|
342
|
-
parseGitStatusPorcelain,
|
|
343
|
-
readGitStatus,
|
|
344
|
-
} from "./git-status.js";
|
|
345
|
-
export {
|
|
346
|
-
contextColor,
|
|
347
|
-
formatCount,
|
|
348
|
-
formatToolActivity,
|
|
349
|
-
prContextFromStatuses,
|
|
350
|
-
prLinkFromStatuses,
|
|
351
|
-
shortenModel,
|
|
352
|
-
} from "./render.js";
|
|
353
|
-
export { normalizeStatuslineSettings, readStatuslineSettings } from "./settings.js";
|
package/src/terminal.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export function sanitizeTerminalText(value: string): string {
|
|
2
|
-
let result = "";
|
|
3
|
-
for (let index = 0; index < value.length; ) {
|
|
4
|
-
const codePoint = value.codePointAt(index) ?? 0;
|
|
5
|
-
const character = String.fromCodePoint(codePoint);
|
|
6
|
-
if (codePoint === 0x1b || codePoint === 0x9b || codePoint === 0x9d) {
|
|
7
|
-
index = skipTerminalEscape(value, index, codePoint);
|
|
8
|
-
continue;
|
|
9
|
-
}
|
|
10
|
-
if (
|
|
11
|
-
codePoint <= 0x1f ||
|
|
12
|
-
(codePoint >= 0x7f && codePoint <= 0x9f) ||
|
|
13
|
-
codePoint === 0x2028 ||
|
|
14
|
-
codePoint === 0x2029
|
|
15
|
-
) {
|
|
16
|
-
if (
|
|
17
|
-
codePoint === 0x09 ||
|
|
18
|
-
codePoint === 0x0a ||
|
|
19
|
-
codePoint === 0x0d ||
|
|
20
|
-
codePoint === 0x85 ||
|
|
21
|
-
codePoint === 0x2028 ||
|
|
22
|
-
codePoint === 0x2029
|
|
23
|
-
) {
|
|
24
|
-
result += " ";
|
|
25
|
-
}
|
|
26
|
-
index += character.length;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
result += character;
|
|
30
|
-
index += character.length;
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function skipTerminalEscape(value: string, start: number, codePoint: number): number {
|
|
36
|
-
let index = start + 1;
|
|
37
|
-
const next = value.charCodeAt(index);
|
|
38
|
-
const isOsc = codePoint === 0x9d || (codePoint === 0x1b && next === 0x5d);
|
|
39
|
-
if (isOsc) {
|
|
40
|
-
if (codePoint === 0x1b) index += 1;
|
|
41
|
-
while (index < value.length) {
|
|
42
|
-
const current = value.charCodeAt(index);
|
|
43
|
-
if (current === 0x07 || current === 0x9c) return index + 1;
|
|
44
|
-
if (current === 0x1b && value.charCodeAt(index + 1) === 0x5c) return index + 2;
|
|
45
|
-
index += 1;
|
|
46
|
-
}
|
|
47
|
-
return index;
|
|
48
|
-
}
|
|
49
|
-
const isCsi = codePoint === 0x9b || (codePoint === 0x1b && next === 0x5b);
|
|
50
|
-
if (isCsi) {
|
|
51
|
-
if (codePoint === 0x1b) index += 1;
|
|
52
|
-
while (index < value.length) {
|
|
53
|
-
const current = value.charCodeAt(index);
|
|
54
|
-
index += 1;
|
|
55
|
-
if (current >= 0x40 && current <= 0x7e) break;
|
|
56
|
-
}
|
|
57
|
-
return index;
|
|
58
|
-
}
|
|
59
|
-
return Math.min(value.length, start + (codePoint === 0x1b ? 2 : 1));
|
|
60
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
3
|
-
export const SEGMENT_NAMES = [
|
|
4
|
-
"brand",
|
|
5
|
-
"provider",
|
|
6
|
-
"model",
|
|
7
|
-
"thinking",
|
|
8
|
-
"cwd",
|
|
9
|
-
"branch",
|
|
10
|
-
"tools",
|
|
11
|
-
"context",
|
|
12
|
-
"tokens",
|
|
13
|
-
"cache",
|
|
14
|
-
"cost",
|
|
15
|
-
"time",
|
|
16
|
-
"turn",
|
|
17
|
-
] as const;
|
|
18
|
-
export type SegmentName = (typeof SEGMENT_NAMES)[number];
|
|
19
|
-
|
|
20
|
-
export const LINE_BREAK_SEGMENT_NAME = "line_break" as const;
|
|
21
|
-
export type ConfigSegmentName = SegmentName | typeof LINE_BREAK_SEGMENT_NAME;
|
|
22
|
-
|
|
23
|
-
export const PALETTE_NAMES = [
|
|
24
|
-
"tokyo-night",
|
|
25
|
-
"ocean",
|
|
26
|
-
"sunset",
|
|
27
|
-
"forest",
|
|
28
|
-
"candy",
|
|
29
|
-
"neon",
|
|
30
|
-
"mono",
|
|
31
|
-
] as const;
|
|
32
|
-
export type PaletteName = (typeof PALETTE_NAMES)[number];
|
|
33
|
-
|
|
34
|
-
export const PALETTE_PRESET_NAMES = [...PALETTE_NAMES, "custom"] as const;
|
|
35
|
-
export type PalettePreset = (typeof PALETTE_PRESET_NAMES)[number];
|
|
36
|
-
|
|
37
|
-
export const DENSITIES = ["compact", "cozy"] as const;
|
|
38
|
-
export type Density = (typeof DENSITIES)[number];
|
|
39
|
-
|
|
40
|
-
export const SEPARATOR_NAMES = ["none", "dot", "bar", "powerline", "round"] as const;
|
|
41
|
-
export type SeparatorName = (typeof SEPARATOR_NAMES)[number];
|
|
42
|
-
|
|
43
|
-
export const TRUNCATION_DIRECTIONS = ["start", "middle", "end"] as const;
|
|
44
|
-
export type TruncationDirection = (typeof TRUNCATION_DIRECTIONS)[number];
|
|
45
|
-
|
|
46
|
-
export type PowerlineBlockName = "header" | "directory" | "git" | "runtime" | "meter";
|
|
47
|
-
|
|
48
|
-
export interface SegmentTextConfig {
|
|
49
|
-
prefix: string;
|
|
50
|
-
suffix: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface ModelSegmentTextConfig extends SegmentTextConfig {
|
|
54
|
-
truncationLength: number;
|
|
55
|
-
truncationSymbol: string;
|
|
56
|
-
truncationDirection: TruncationDirection;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface SegmentPaletteColor {
|
|
60
|
-
fg?: string;
|
|
61
|
-
bg?: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type SegmentPalette = Partial<Record<SegmentName, SegmentPaletteColor>>;
|
|
65
|
-
|
|
66
|
-
export interface StatuslineConfig {
|
|
67
|
-
palettePreset: PalettePreset;
|
|
68
|
-
palette: SegmentPalette;
|
|
69
|
-
density: Density;
|
|
70
|
-
separator: SeparatorName;
|
|
71
|
-
segments: ConfigSegmentName[];
|
|
72
|
-
segmentText: Record<SegmentName, SegmentTextConfig> & { model: ModelSegmentTextConfig };
|
|
73
|
-
extensionStatusIcons: Record<string, string>;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface RenderSegment {
|
|
77
|
-
name: SegmentName;
|
|
78
|
-
text: string;
|
|
79
|
-
color: ThemeColor;
|
|
80
|
-
block: PowerlineBlockName;
|
|
81
|
-
emphasis?: boolean;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface RenderLineBreak {
|
|
85
|
-
name: typeof LINE_BREAK_SEGMENT_NAME;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export type RenderItem = RenderSegment | RenderLineBreak;
|
package/src/usage.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
3
|
-
interface UsageLike {
|
|
4
|
-
input?: number;
|
|
5
|
-
output?: number;
|
|
6
|
-
cacheRead?: number;
|
|
7
|
-
cacheWrite?: number;
|
|
8
|
-
cost?: { total?: number };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface FooterUsageSummary {
|
|
12
|
-
input: number;
|
|
13
|
-
output: number;
|
|
14
|
-
cacheRead: number;
|
|
15
|
-
cacheWrite: number;
|
|
16
|
-
cost: number;
|
|
17
|
-
latestCacheHitRate?: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function summarizeFooterUsage(entries: readonly SessionEntry[]): FooterUsageSummary {
|
|
21
|
-
const totals: FooterUsageSummary = {
|
|
22
|
-
input: 0,
|
|
23
|
-
output: 0,
|
|
24
|
-
cacheRead: 0,
|
|
25
|
-
cacheWrite: 0,
|
|
26
|
-
cost: 0,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
for (const entry of entries) {
|
|
30
|
-
let usage: UsageLike | undefined;
|
|
31
|
-
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
32
|
-
usage = entry.message.usage;
|
|
33
|
-
const input = usage.input ?? 0;
|
|
34
|
-
const cacheRead = usage.cacheRead ?? 0;
|
|
35
|
-
const cacheWrite = usage.cacheWrite ?? 0;
|
|
36
|
-
const promptTokens = input + cacheRead + cacheWrite;
|
|
37
|
-
totals.latestCacheHitRate = promptTokens > 0 ? (cacheRead / promptTokens) * 100 : undefined;
|
|
38
|
-
} else if (entry.type === "message" && entry.message.role === "toolResult") {
|
|
39
|
-
usage = entry.message.usage;
|
|
40
|
-
} else if (entry.type === "compaction" || entry.type === "branch_summary") {
|
|
41
|
-
usage = entry.usage;
|
|
42
|
-
}
|
|
43
|
-
if (!usage) continue;
|
|
44
|
-
|
|
45
|
-
totals.input += usage.input ?? 0;
|
|
46
|
-
totals.output += usage.output ?? 0;
|
|
47
|
-
totals.cacheRead += usage.cacheRead ?? 0;
|
|
48
|
-
totals.cacheWrite += usage.cacheWrite ?? 0;
|
|
49
|
-
totals.cost += usage.cost?.total ?? 0;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return totals;
|
|
53
|
-
}
|