@ceo.paludetto/pi-starship 0.0.0 → 0.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.
@@ -0,0 +1,21 @@
1
+ $ tsdown
2
+ ℹ tsdown v0.22.3 powered by rolldown v1.1.3
3
+ ℹ config file: /Users/carlos/Developer/JavaScript/PI/packages/pi-starship/tsdown.config.ts
4
+ ℹ entry: ./src/index.ts
5
+ ℹ tsconfig: tsconfig.json
6
+ ℹ Build start
7
+ ℹ [CJS] dist/index.cjs  8.74 kB │ gzip: 3.10 kB
8
+ ℹ [CJS] dist/index.cjs.map 18.51 kB │ gzip: 5.71 kB
9
+ ℹ [CJS] 2 files, total: 27.25 kB
10
+ ℹ [CJS] dist/index.d.cts.map 0.13 kB │ gzip: 0.13 kB
11
+ ℹ [CJS] dist/index.d.cts 0.21 kB │ gzip: 0.17 kB
12
+ ℹ [CJS] 2 files, total: 0.35 kB
13
+ ✔ Build complete in 882ms
14
+ ℹ [ESM] dist/index.mjs  8.62 kB │ gzip: 3.08 kB
15
+ ℹ [ESM] dist/index.mjs.map 18.43 kB │ gzip: 5.68 kB
16
+ ℹ [ESM] dist/index.d.mts.map  0.13 kB │ gzip: 0.13 kB
17
+ ℹ [ESM] dist/index.d.mts  0.24 kB │ gzip: 0.19 kB
18
+ ℹ [ESM] 4 files, total: 27.42 kB
19
+ ✔ Build complete in 883ms
20
+ ℹ [publint] The package does not specify the "engines.node" field. Consumers may install it on an unsupported Node.js version. Consider adding the field with your actual minimum supported Node.js version. (This may be a breaking change)
21
+ ℹ [publint] pkg.repository.url is https://github.com/ceopaludetto/pi but could be a full git URL like "git+https://github.com/ceopaludetto/pi.git".
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @ceo.paludetto/pi-starship
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`bb319d0`](https://github.com/ceopaludetto/pi/commit/bb319d0567ad381b434c6b78a72404ee8b96db59) - Add initial functionality
8
+
9
+ - [`bb319d0`](https://github.com/ceopaludetto/pi/commit/bb319d0567ad381b434c6b78a72404ee8b96db59) - Add more modules to starship
package/dist/index.cjs ADDED
@@ -0,0 +1,277 @@
1
+ let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
2
+ let node_child_process = require("node:child_process");
3
+ let node_util = require("node:util");
4
+ //#region ../pi-utilities/dist/index.mjs
5
+ const bold = (text) => `\x1B[1m${text}\x1B[22m`;
6
+ const cyan = (text) => `\x1B[96m${text}\x1B[39m`;
7
+ const dim = (text) => `\x1B[2m${text}\x1B[22m`;
8
+ const green = (text) => `\x1B[92m${text}\x1B[39m`;
9
+ const magenta = (text) => `\x1B[95m${text}\x1B[39m`;
10
+ const yellow = (text) => `\x1B[93m${text}\x1B[39m`;
11
+ const RAINBOW_COLORS = [
12
+ magenta,
13
+ yellow,
14
+ green,
15
+ cyan
16
+ ];
17
+ function rainbow(text) {
18
+ return [...text].map((character, index) => RAINBOW_COLORS[index % RAINBOW_COLORS.length](character)).join("");
19
+ }
20
+ new RegExp(`\\[[0-9;]*m`, "g");
21
+ const PROVIDER_NAME_OVERRIDES = {
22
+ "amazon": "Amazon",
23
+ "anthropic": "Anthropic",
24
+ "azure": "Azure",
25
+ "cloudflare": "Cloudflare",
26
+ "google": "Google",
27
+ "mistral": "Mistral",
28
+ "openai": "OpenAI",
29
+ "opencode-go": "Opencode Go",
30
+ "opencode": "Opencode",
31
+ "openrouter": "OpenRouter"
32
+ };
33
+ function capitalizeFirst(text) {
34
+ if (!text) return text;
35
+ return text.charAt(0).toUpperCase() + text.slice(1);
36
+ }
37
+ function formatProviderName(provider) {
38
+ if (!provider) return provider;
39
+ const override = PROVIDER_NAME_OVERRIDES[provider];
40
+ if (override) return override;
41
+ return provider.charAt(0).toUpperCase() + provider.slice(1);
42
+ }
43
+ function formatTokenCount(count) {
44
+ if (count < 1e3) return `${count}`;
45
+ return `${(count / 1e3).toFixed(1)}k`;
46
+ }
47
+ //#endregion
48
+ //#region src/utilities/types.ts
49
+ var Module = class {
50
+ context;
51
+ constructor(context) {
52
+ this.context = context;
53
+ }
54
+ getStatuses() {
55
+ return this.context.getStatuses();
56
+ }
57
+ async refresh() {}
58
+ };
59
+ //#endregion
60
+ //#region src/modules/mcp.module.ts
61
+ /** Status key published by pi-mcp-adapter (e.g. "MCP: 2/3 servers"). */
62
+ const MCP_STATUS_KEY = "mcp";
63
+ const SERVER_RATIO_PATTERN = /(\d+)\/(\d+)/;
64
+ var McpModule = class extends Module {
65
+ render() {
66
+ const status = this.getStatuses().get(MCP_STATUS_KEY);
67
+ if (!status) return null;
68
+ const match = SERVER_RATIO_PATTERN.exec(status);
69
+ if (!match) return null;
70
+ return `${cyan("MCP")} ${dim("(")}${match[1]}/${match[2]}${dim(")")}`;
71
+ }
72
+ };
73
+ //#endregion
74
+ //#region src/modules/mode.module.ts
75
+ const REQUEST_CHANNEL = "plannotator:request";
76
+ function createPhaseTracker(pi, onPhaseChange) {
77
+ let currentPhase = null;
78
+ function applyPhaseFromResponse(response) {
79
+ if (response?.status !== "handled" || !response.result?.phase) return;
80
+ if (currentPhase === response.result.phase) return;
81
+ currentPhase = response.result.phase;
82
+ onPhaseChange();
83
+ }
84
+ pi.events.on(REQUEST_CHANNEL, (data) => {
85
+ if (data?.action !== "plan-mode" || typeof data?.respond !== "function") return;
86
+ const originalRespond = data.respond;
87
+ data.respond = (response) => {
88
+ originalRespond(response);
89
+ applyPhaseFromResponse(response);
90
+ };
91
+ });
92
+ function query() {
93
+ pi.events.emit(REQUEST_CHANNEL, {
94
+ requestId: Math.random().toString(36).slice(2),
95
+ action: "plan-mode",
96
+ payload: { mode: "status" },
97
+ respond: (response) => applyPhaseFromResponse(response)
98
+ });
99
+ }
100
+ return {
101
+ query,
102
+ getPhase: () => currentPhase
103
+ };
104
+ }
105
+ var ModeModule = class extends Module {
106
+ phaseTracker;
107
+ constructor(context, phaseTracker) {
108
+ super(context);
109
+ this.phaseTracker = phaseTracker;
110
+ }
111
+ render() {
112
+ const phase = this.phaseTracker.getPhase();
113
+ if (phase === null) return null;
114
+ return phase !== "idle" ? yellow("Plan") : green("Build");
115
+ }
116
+ };
117
+ //#endregion
118
+ //#region src/modules/model.module.ts
119
+ var ModelModule = class extends Module {
120
+ render() {
121
+ const model = this.context.model;
122
+ if (!model) return null;
123
+ const provider = formatProviderName(model.provider);
124
+ const thinkingLevel = this.context.getThinkingLevel();
125
+ const parenthesisContent = thinkingLevel !== "off" ? `${provider}${dim("/")}${cyan(capitalizeFirst(thinkingLevel))}` : provider;
126
+ return `${bold(magenta(model.name))}${dim(" (")}${parenthesisContent}${dim(")")}`;
127
+ }
128
+ };
129
+ //#endregion
130
+ //#region src/modules/starship.module.ts
131
+ const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
132
+ const STARSHIP_ARGUMENTS = [
133
+ "--status=0",
134
+ "--keymap=",
135
+ "--pipestatus=0",
136
+ "--cmd-duration=0",
137
+ "--jobs=0"
138
+ ];
139
+ const ESCAPE_CHARACTER = "\x1B";
140
+ const TRAILING_SGR_PATTERN = new RegExp(`${ESCAPE_CHARACTER}\\[[0-9;]*m+$`, "g");
141
+ var StarshipModule = class extends Module {
142
+ prompt = null;
143
+ async refresh() {
144
+ this.prompt = await this.fetchStarshipPrompt();
145
+ }
146
+ render() {
147
+ return this.prompt;
148
+ }
149
+ async fetchStarshipPrompt() {
150
+ try {
151
+ const { stdout } = await execFileAsync("starship", [
152
+ "prompt",
153
+ `--terminal-width=${this.context.getWidth()}`,
154
+ ...STARSHIP_ARGUMENTS
155
+ ], {
156
+ cwd: this.context.cwd,
157
+ timeout: 3e3,
158
+ env: {
159
+ ...process.env,
160
+ PWD: this.context.cwd,
161
+ STARSHIP_SHELL: "bash"
162
+ }
163
+ });
164
+ return this.cleanStarshipOutput(stdout);
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+ cleanStarshipOutput(stdout) {
170
+ return (stdout.split("\n")[0] ?? "").replace(/\\\[/g, "").replace(/\\\]/g, "").replace(/%\{/g, ESCAPE_CHARACTER).replace(/%\}/g, "").replace(TRAILING_SGR_PATTERN, "").trimEnd() || null;
171
+ }
172
+ };
173
+ //#endregion
174
+ //#region src/modules/tokens.module.ts
175
+ var TokensModule = class extends Module {
176
+ render() {
177
+ const { inputTokens, outputTokens, totalCost } = this.aggregateUsage(this.context.sessionManager.getBranch());
178
+ if (inputTokens === 0 && outputTokens === 0) return null;
179
+ return [
180
+ cyan(`↑${formatTokenCount(inputTokens)}`),
181
+ green(`↓${formatTokenCount(outputTokens)}`),
182
+ yellow(`$${totalCost.toFixed(3)}`)
183
+ ].join(dim("/"));
184
+ }
185
+ aggregateUsage(branch) {
186
+ let inputTokens = 0;
187
+ let outputTokens = 0;
188
+ let totalCost = 0;
189
+ for (const entry of branch) {
190
+ if (entry.type !== "message" || entry.message.role !== "assistant") continue;
191
+ const message = entry.message;
192
+ inputTokens += message.usage.input;
193
+ outputTokens += message.usage.output;
194
+ totalCost += message.usage.cost.total;
195
+ }
196
+ return {
197
+ inputTokens,
198
+ outputTokens,
199
+ totalCost
200
+ };
201
+ }
202
+ };
203
+ //#endregion
204
+ //#region src/modules/yolo.module.ts
205
+ /** Published by @gotgenes/pi-permission-system via ctx.ui.setStatus(). */
206
+ const PERMISSION_SYSTEM_STATUS_KEY = "pi-permission-system";
207
+ const PERMISSION_SYSTEM_YOLO_STATUS_VALUE = "yolo";
208
+ var YoloModule = class extends Module {
209
+ render() {
210
+ if (this.getStatuses().get(PERMISSION_SYSTEM_STATUS_KEY) !== PERMISSION_SYSTEM_YOLO_STATUS_VALUE) return null;
211
+ return bold(rainbow("Yolo"));
212
+ }
213
+ };
214
+ //#endregion
215
+ //#region src/index.ts
216
+ const MODULE_SEPARATOR = dim(" ⋅ ");
217
+ const HORIZONTAL_PADDING = " ";
218
+ function StarshipExtension(pi) {
219
+ let thinkingLevel = "off";
220
+ let lastRenderWidth = 120;
221
+ let requestRender;
222
+ let refreshAll;
223
+ let extensionStatuses = /* @__PURE__ */ new Map();
224
+ const phaseTracker = createPhaseTracker(pi, () => requestRender?.());
225
+ pi.on("session_start", async (_, context) => {
226
+ thinkingLevel = pi.getThinkingLevel();
227
+ const moduleContext = {
228
+ ...context,
229
+ getStatuses: () => extensionStatuses,
230
+ getThinkingLevel: () => thinkingLevel,
231
+ getWidth: () => lastRenderWidth
232
+ };
233
+ const leftModules = [new StarshipModule(moduleContext), new McpModule(moduleContext)];
234
+ const rightModules = [
235
+ new YoloModule(moduleContext),
236
+ new ModeModule(moduleContext, phaseTracker),
237
+ new ModelModule(moduleContext),
238
+ new TokensModule(moduleContext)
239
+ ];
240
+ const allModules = [...leftModules, ...rightModules];
241
+ refreshAll = async () => {
242
+ await Promise.all(allModules.map((item) => item.refresh()));
243
+ requestRender?.();
244
+ };
245
+ await refreshAll();
246
+ context.ui.setFooter((tui, _, data) => {
247
+ requestRender = () => tui.requestRender();
248
+ return {
249
+ dispose: data.onBranchChange(() => refreshAll?.()),
250
+ invalidate() {},
251
+ render(width) {
252
+ extensionStatuses = data.getExtensionStatuses();
253
+ phaseTracker.query();
254
+ if (width !== lastRenderWidth) {
255
+ lastRenderWidth = width;
256
+ for (const item of leftModules) item.refresh();
257
+ }
258
+ const contentWidth = width - 2;
259
+ const left = leftModules.map((item) => item.render()).filter((value) => value !== null).join(MODULE_SEPARATOR);
260
+ const right = rightModules.map((item) => item.render()).filter((value) => value !== null).join(MODULE_SEPARATOR);
261
+ return [HORIZONTAL_PADDING + (0, _earendil_works_pi_tui.truncateToWidth)(left + " ".repeat(Math.max(1, contentWidth - (0, _earendil_works_pi_tui.visibleWidth)(left) - (0, _earendil_works_pi_tui.visibleWidth)(right))) + right, contentWidth) + HORIZONTAL_PADDING];
262
+ }
263
+ };
264
+ });
265
+ });
266
+ pi.on("agent_end", () => {
267
+ refreshAll?.();
268
+ });
269
+ pi.on("thinking_level_select", (event, _) => {
270
+ thinkingLevel = event.level;
271
+ requestRender?.();
272
+ });
273
+ }
274
+ //#endregion
275
+ module.exports = StarshipExtension;
276
+
277
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["execFile"],"sources":["../../pi-utilities/dist/index.mjs","../src/utilities/types.ts","../src/modules/mcp.module.ts","../src/modules/mode.module.ts","../src/modules/model.module.ts","../src/modules/starship.module.ts","../src/modules/tokens.module.ts","../src/modules/yolo.module.ts","../src/index.ts"],"sourcesContent":["//#region src/color.ts\nconst bold = (text) => `\\x1B[1m${text}\\x1B[22m`;\nconst cyan = (text) => `\\x1B[96m${text}\\x1B[39m`;\nconst dim = (text) => `\\x1B[2m${text}\\x1B[22m`;\nconst green = (text) => `\\x1B[92m${text}\\x1B[39m`;\nconst magenta = (text) => `\\x1B[95m${text}\\x1B[39m`;\nconst yellow = (text) => `\\x1B[93m${text}\\x1B[39m`;\nfunction hyperlink(url, text) {\n\treturn `\\x1B]8;;${url}\\x1B\\\\${text}\\x1B]8;;\\x1B\\\\`;\n}\nconst RAINBOW_COLORS = [\n\tmagenta,\n\tyellow,\n\tgreen,\n\tcyan\n];\nfunction rainbow(text) {\n\treturn [...text].map((character, index) => RAINBOW_COLORS[index % RAINBOW_COLORS.length](character)).join(\"\");\n}\n//#endregion\n//#region src/format.ts\nconst ANSI_SGR_PATTERN = new RegExp(`\u001b\\\\[[0-9;]*m`, \"g\");\nconst PROVIDER_NAME_OVERRIDES = {\n\t\"amazon\": \"Amazon\",\n\t\"anthropic\": \"Anthropic\",\n\t\"azure\": \"Azure\",\n\t\"cloudflare\": \"Cloudflare\",\n\t\"google\": \"Google\",\n\t\"mistral\": \"Mistral\",\n\t\"openai\": \"OpenAI\",\n\t\"opencode-go\": \"Opencode Go\",\n\t\"opencode\": \"Opencode\",\n\t\"openrouter\": \"OpenRouter\"\n};\nfunction capitalizeFirst(text) {\n\tif (!text) return text;\n\treturn text.charAt(0).toUpperCase() + text.slice(1);\n}\nfunction formatProviderName(provider) {\n\tif (!provider) return provider;\n\tconst override = PROVIDER_NAME_OVERRIDES[provider];\n\tif (override) return override;\n\treturn provider.charAt(0).toUpperCase() + provider.slice(1);\n}\nfunction formatTokenCount(count) {\n\tif (count < 1e3) return `${count}`;\n\treturn `${(count / 1e3).toFixed(1)}k`;\n}\nfunction stripAnsi(text) {\n\treturn text.replace(ANSI_SGR_PATTERN, \"\");\n}\n//#endregion\nexport { bold, capitalizeFirst, cyan, dim, formatProviderName, formatTokenCount, green, hyperlink, magenta, rainbow, stripAnsi, yellow };\n\n//# sourceMappingURL=index.mjs.map","import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\n\nexport type ModuleContext = {\n\tgetStatuses: () => ReadonlyMap<string, string>;\n\tgetThinkingLevel: () => string;\n\tgetWidth: () => number;\n} & ExtensionContext;\n\nexport abstract class Module {\n\tpublic constructor(\n\t\tprotected readonly context: ModuleContext,\n\t) {}\n\n\tprotected getStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.context.getStatuses();\n\t}\n\n\tpublic abstract render(): string | null;\n\n\tpublic async refresh(): Promise<void> {}\n}\n","import { cyan, dim } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\n/** Status key published by pi-mcp-adapter (e.g. \"MCP: 2/3 servers\"). */\nconst MCP_STATUS_KEY = \"mcp\";\nconst SERVER_RATIO_PATTERN = /(\\d+)\\/(\\d+)/;\n\nexport class McpModule extends Module {\n\tpublic override render(): string | null {\n\t\tconst status = this.getStatuses().get(MCP_STATUS_KEY);\n\n\t\tif (!status)\n\t\t\treturn null;\n\n\t\tconst match = SERVER_RATIO_PATTERN.exec(status);\n\n\t\tif (!match)\n\t\t\treturn null;\n\n\t\treturn `${cyan(\"MCP\")} ${dim(\"(\")}${match[1]}/${match[2]}${dim(\")\")}`;\n\t}\n}\n","import type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\nimport type { ModuleContext } from \"~/utilities/types\";\n\nimport { green, yellow } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\nconst REQUEST_CHANNEL = \"plannotator:request\";\n\ntype PlannotatorPhase = \"idle\" | \"planning\" | \"executing\";\n\nexport type PhaseTracker = {\n\tquery: () => void;\n\tgetPhase: () => PlannotatorPhase | null;\n};\n\nexport function createPhaseTracker(pi: ExtensionAPI, onPhaseChange: () => void): PhaseTracker {\n\tlet currentPhase: PlannotatorPhase | null = null;\n\n\tfunction applyPhaseFromResponse(response: any) {\n\t\tif (response?.status !== \"handled\" || !response.result?.phase)\n\t\t\treturn;\n\t\tif (currentPhase === response.result.phase)\n\t\t\treturn;\n\n\t\tcurrentPhase = response.result.phase;\n\t\tonPhaseChange();\n\t}\n\n\tpi.events.on(REQUEST_CHANNEL, (data: any) => {\n\t\tif (data?.action !== \"plan-mode\" || typeof data?.respond !== \"function\")\n\t\t\treturn;\n\n\t\tconst originalRespond = data.respond;\n\n\t\tdata.respond = (response: any) => {\n\t\t\toriginalRespond(response);\n\t\t\tapplyPhaseFromResponse(response);\n\t\t};\n\t});\n\n\tfunction query() {\n\t\tpi.events.emit(REQUEST_CHANNEL, {\n\t\t\trequestId: Math.random().toString(36).slice(2),\n\t\t\taction: \"plan-mode\",\n\t\t\tpayload: { mode: \"status\" },\n\t\t\trespond: (response: any) => applyPhaseFromResponse(response),\n\t\t});\n\t}\n\n\treturn { query, getPhase: () => currentPhase };\n}\n\nexport class ModeModule extends Module {\n\tpublic constructor(context: ModuleContext, private readonly phaseTracker: PhaseTracker) {\n\t\tsuper(context);\n\t}\n\n\tpublic override render(): string | null {\n\t\tconst phase = this.phaseTracker.getPhase();\n\n\t\tif (phase === null)\n\t\t\treturn null;\n\n\t\treturn phase !== \"idle\" ? yellow(\"Plan\") : green(\"Build\");\n\t}\n}\n","import { bold, capitalizeFirst, cyan, dim, formatProviderName, magenta } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\nexport class ModelModule extends Module {\n\tpublic override render(): string | null {\n\t\tconst model = this.context.model;\n\n\t\tif (!model)\n\t\t\treturn null;\n\n\t\tconst provider = formatProviderName(model.provider);\n\t\tconst thinkingLevel = this.context.getThinkingLevel();\n\n\t\tconst parenthesisContent = thinkingLevel !== \"off\"\n\t\t\t? `${provider}${dim(\"/\")}${cyan(capitalizeFirst(thinkingLevel))}`\n\t\t\t: provider;\n\n\t\treturn `${bold(magenta(model.name))}${dim(\" (\")}${parenthesisContent}${dim(\")\")}`;\n\t}\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { Module } from \"~/utilities/types\";\n\nconst execFileAsync = promisify(execFile);\n\nconst STARSHIP_ARGUMENTS = [\n\t\"--status=0\",\n\t\"--keymap=\",\n\t\"--pipestatus=0\",\n\t\"--cmd-duration=0\",\n\t\"--jobs=0\",\n];\n\nconst ESCAPE_CHARACTER = \"\\x1B\";\nconst TRAILING_SGR_PATTERN = new RegExp(`${ESCAPE_CHARACTER}\\\\[[0-9;]*m+$`, \"g\");\n\nexport class StarshipModule extends Module {\n\tprivate prompt: string | null = null;\n\n\tpublic override async refresh(): Promise<void> {\n\t\tthis.prompt = await this.fetchStarshipPrompt();\n\t}\n\n\tpublic override render(): string | null {\n\t\treturn this.prompt;\n\t}\n\n\tprivate async fetchStarshipPrompt(): Promise<string | null> {\n\t\ttry {\n\t\t\tconst { stdout } = await execFileAsync(\n\t\t\t\t\"starship\",\n\t\t\t\t[\"prompt\", `--terminal-width=${this.context.getWidth()}`, ...STARSHIP_ARGUMENTS],\n\t\t\t\t{\n\t\t\t\t\tcwd: this.context.cwd,\n\t\t\t\t\ttimeout: 3000,\n\t\t\t\t\tenv: { ...process.env, PWD: this.context.cwd, STARSHIP_SHELL: \"bash\" },\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn this.cleanStarshipOutput(stdout);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate cleanStarshipOutput(stdout: string): string | null {\n\t\tconst firstLine = stdout.split(\"\\n\")[0] ?? \"\";\n\n\t\tconst withAnsiEscapes = firstLine\n\t\t\t.replace(/\\\\\\[/g, \"\")\n\t\t\t.replace(/\\\\\\]/g, \"\")\n\t\t\t.replace(/%\\{/g, ESCAPE_CHARACTER)\n\t\t\t.replace(/%\\}/g, \"\");\n\n\t\tconst trimmed = withAnsiEscapes\n\t\t\t.replace(TRAILING_SGR_PATTERN, \"\")\n\t\t\t.trimEnd();\n\n\t\treturn trimmed || null;\n\t}\n}\n","import type { AssistantMessage } from \"@earendil-works/pi-ai\";\n\nimport { cyan, dim, formatTokenCount, green, yellow } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\nexport class TokensModule extends Module {\n\tpublic override render(): string | null {\n\t\tconst { inputTokens, outputTokens, totalCost } = this.aggregateUsage(this.context.sessionManager.getBranch());\n\n\t\tif (inputTokens === 0 && outputTokens === 0)\n\t\t\treturn null;\n\n\t\treturn [\n\t\t\tcyan(`↑${formatTokenCount(inputTokens)}`),\n\t\t\tgreen(`↓${formatTokenCount(outputTokens)}`),\n\t\t\tyellow(`$${totalCost.toFixed(3)}`),\n\t\t].join(dim(\"/\"));\n\t}\n\n\tprivate aggregateUsage(branch: any[]): { inputTokens: number; outputTokens: number; totalCost: number } {\n\t\tlet inputTokens = 0;\n\t\tlet outputTokens = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of branch) {\n\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\")\n\t\t\t\tcontinue;\n\n\t\t\tconst message = entry.message as AssistantMessage;\n\t\t\tinputTokens += message.usage.input;\n\t\t\toutputTokens += message.usage.output;\n\t\t\ttotalCost += message.usage.cost.total;\n\t\t}\n\n\t\treturn { inputTokens, outputTokens, totalCost };\n\t}\n}\n","import { bold, rainbow } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\n/** Published by @gotgenes/pi-permission-system via ctx.ui.setStatus(). */\nconst PERMISSION_SYSTEM_STATUS_KEY = \"pi-permission-system\";\nconst PERMISSION_SYSTEM_YOLO_STATUS_VALUE = \"yolo\";\n\nexport class YoloModule extends Module {\n\tpublic override render(): string | null {\n\t\tif (this.getStatuses().get(PERMISSION_SYSTEM_STATUS_KEY) !== PERMISSION_SYSTEM_YOLO_STATUS_VALUE)\n\t\t\treturn null;\n\n\t\treturn bold(rainbow(\"Yolo\"));\n\t}\n}\n","import type { Module, ModuleContext } from \"./utilities/types\";\nimport type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\n\nimport { dim } from \"@ceo.paludetto/pi-utilities\";\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\n\nimport { McpModule } from \"./modules/mcp.module\";\nimport { createPhaseTracker, ModeModule } from \"./modules/mode.module\";\nimport { ModelModule } from \"./modules/model.module\";\nimport { StarshipModule } from \"./modules/starship.module\";\nimport { TokensModule } from \"./modules/tokens.module\";\nimport { YoloModule } from \"./modules/yolo.module\";\n\nconst MODULE_SEPARATOR = dim(\" ⋅ \");\nconst HORIZONTAL_PADDING = \" \";\n\nexport default function StarshipExtension(pi: ExtensionAPI) {\n\tlet thinkingLevel = \"off\";\n\tlet lastRenderWidth = 120;\n\tlet requestRender: (() => void) | undefined;\n\tlet refreshAll: (() => Promise<void>) | undefined;\n\tlet extensionStatuses: ReadonlyMap<string, string> = new Map();\n\n\tconst phaseTracker = createPhaseTracker(pi, () => requestRender?.());\n\n\tpi.on(\"session_start\", async (_, context) => {\n\t\tthinkingLevel = pi.getThinkingLevel();\n\n\t\tconst moduleContext: ModuleContext = {\n\t\t\t...context,\n\t\t\tgetStatuses: () => extensionStatuses,\n\t\t\tgetThinkingLevel: () => thinkingLevel,\n\t\t\tgetWidth: () => lastRenderWidth,\n\t\t};\n\n\t\tconst leftModules: Module[] = [\n\t\t\tnew StarshipModule(moduleContext),\n\t\t\tnew McpModule(moduleContext),\n\t\t];\n\n\t\tconst rightModules: Module[] = [\n\t\t\tnew YoloModule(moduleContext),\n\t\t\tnew ModeModule(moduleContext, phaseTracker),\n\t\t\tnew ModelModule(moduleContext),\n\t\t\tnew TokensModule(moduleContext),\n\t\t];\n\n\t\tconst allModules = [...leftModules, ...rightModules];\n\n\t\trefreshAll = async () => {\n\t\t\tawait Promise.all(allModules.map((item) => item.refresh()));\n\t\t\trequestRender?.();\n\t\t};\n\n\t\tawait refreshAll();\n\n\t\tcontext.ui.setFooter((tui, _, data) => {\n\t\t\trequestRender = () => tui.requestRender();\n\t\t\tconst unsubscribe = data.onBranchChange(() => refreshAll?.());\n\n\t\t\treturn {\n\t\t\t\tdispose: unsubscribe,\n\t\t\t\tinvalidate() {},\n\n\t\t\t\trender(width: number): string[] {\n\t\t\t\t\textensionStatuses = data.getExtensionStatuses();\n\t\t\t\t\tphaseTracker.query();\n\n\t\t\t\t\tif (width !== lastRenderWidth) {\n\t\t\t\t\t\tlastRenderWidth = width;\n\t\t\t\t\t\tfor (const item of leftModules)\n\t\t\t\t\t\t\titem.refresh();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst contentWidth = width - 2;\n\n\t\t\t\t\tconst left = leftModules\n\t\t\t\t\t\t.map((item) => item.render())\n\t\t\t\t\t\t.filter((value): value is string => value !== null)\n\t\t\t\t\t\t.join(MODULE_SEPARATOR);\n\n\t\t\t\t\tconst right = rightModules\n\t\t\t\t\t\t.map((item) => item.render())\n\t\t\t\t\t\t.filter((value): value is string => value !== null)\n\t\t\t\t\t\t.join(MODULE_SEPARATOR);\n\n\t\t\t\t\tconst gap = \" \".repeat(Math.max(1, contentWidth - visibleWidth(left) - visibleWidth(right)));\n\t\t\t\t\treturn [HORIZONTAL_PADDING + truncateToWidth(left + gap + right, contentWidth) + HORIZONTAL_PADDING];\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t});\n\n\tpi.on(\"agent_end\", () => {\n\t\trefreshAll?.();\n\t});\n\n\tpi.on(\"thinking_level_select\", (event, _) => {\n\t\tthinkingLevel = event.level;\n\t\trequestRender?.();\n\t});\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+
3
+ //#region src/index.d.ts
4
+ declare function StarshipExtension(pi: ExtensionAPI): void;
5
+ export = StarshipExtension;
6
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBAgBwB,iBAAA,CAAkB,EAAgB,EAAZ,YAAY;AAAA"}
@@ -0,0 +1,7 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+
3
+ //#region src/index.d.ts
4
+ declare function StarshipExtension(pi: ExtensionAPI): void;
5
+ //#endregion
6
+ export { StarshipExtension as default };
7
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBAgBwB,iBAAA,CAAkB,EAAgB,EAAZ,YAAY"}
package/dist/index.mjs ADDED
@@ -0,0 +1,277 @@
1
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { execFile } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ //#region ../pi-utilities/dist/index.mjs
5
+ const bold = (text) => `\x1B[1m${text}\x1B[22m`;
6
+ const cyan = (text) => `\x1B[96m${text}\x1B[39m`;
7
+ const dim = (text) => `\x1B[2m${text}\x1B[22m`;
8
+ const green = (text) => `\x1B[92m${text}\x1B[39m`;
9
+ const magenta = (text) => `\x1B[95m${text}\x1B[39m`;
10
+ const yellow = (text) => `\x1B[93m${text}\x1B[39m`;
11
+ const RAINBOW_COLORS = [
12
+ magenta,
13
+ yellow,
14
+ green,
15
+ cyan
16
+ ];
17
+ function rainbow(text) {
18
+ return [...text].map((character, index) => RAINBOW_COLORS[index % RAINBOW_COLORS.length](character)).join("");
19
+ }
20
+ new RegExp(`\\[[0-9;]*m`, "g");
21
+ const PROVIDER_NAME_OVERRIDES = {
22
+ "amazon": "Amazon",
23
+ "anthropic": "Anthropic",
24
+ "azure": "Azure",
25
+ "cloudflare": "Cloudflare",
26
+ "google": "Google",
27
+ "mistral": "Mistral",
28
+ "openai": "OpenAI",
29
+ "opencode-go": "Opencode Go",
30
+ "opencode": "Opencode",
31
+ "openrouter": "OpenRouter"
32
+ };
33
+ function capitalizeFirst(text) {
34
+ if (!text) return text;
35
+ return text.charAt(0).toUpperCase() + text.slice(1);
36
+ }
37
+ function formatProviderName(provider) {
38
+ if (!provider) return provider;
39
+ const override = PROVIDER_NAME_OVERRIDES[provider];
40
+ if (override) return override;
41
+ return provider.charAt(0).toUpperCase() + provider.slice(1);
42
+ }
43
+ function formatTokenCount(count) {
44
+ if (count < 1e3) return `${count}`;
45
+ return `${(count / 1e3).toFixed(1)}k`;
46
+ }
47
+ //#endregion
48
+ //#region src/utilities/types.ts
49
+ var Module = class {
50
+ context;
51
+ constructor(context) {
52
+ this.context = context;
53
+ }
54
+ getStatuses() {
55
+ return this.context.getStatuses();
56
+ }
57
+ async refresh() {}
58
+ };
59
+ //#endregion
60
+ //#region src/modules/mcp.module.ts
61
+ /** Status key published by pi-mcp-adapter (e.g. "MCP: 2/3 servers"). */
62
+ const MCP_STATUS_KEY = "mcp";
63
+ const SERVER_RATIO_PATTERN = /(\d+)\/(\d+)/;
64
+ var McpModule = class extends Module {
65
+ render() {
66
+ const status = this.getStatuses().get(MCP_STATUS_KEY);
67
+ if (!status) return null;
68
+ const match = SERVER_RATIO_PATTERN.exec(status);
69
+ if (!match) return null;
70
+ return `${cyan("MCP")} ${dim("(")}${match[1]}/${match[2]}${dim(")")}`;
71
+ }
72
+ };
73
+ //#endregion
74
+ //#region src/modules/mode.module.ts
75
+ const REQUEST_CHANNEL = "plannotator:request";
76
+ function createPhaseTracker(pi, onPhaseChange) {
77
+ let currentPhase = null;
78
+ function applyPhaseFromResponse(response) {
79
+ if (response?.status !== "handled" || !response.result?.phase) return;
80
+ if (currentPhase === response.result.phase) return;
81
+ currentPhase = response.result.phase;
82
+ onPhaseChange();
83
+ }
84
+ pi.events.on(REQUEST_CHANNEL, (data) => {
85
+ if (data?.action !== "plan-mode" || typeof data?.respond !== "function") return;
86
+ const originalRespond = data.respond;
87
+ data.respond = (response) => {
88
+ originalRespond(response);
89
+ applyPhaseFromResponse(response);
90
+ };
91
+ });
92
+ function query() {
93
+ pi.events.emit(REQUEST_CHANNEL, {
94
+ requestId: Math.random().toString(36).slice(2),
95
+ action: "plan-mode",
96
+ payload: { mode: "status" },
97
+ respond: (response) => applyPhaseFromResponse(response)
98
+ });
99
+ }
100
+ return {
101
+ query,
102
+ getPhase: () => currentPhase
103
+ };
104
+ }
105
+ var ModeModule = class extends Module {
106
+ phaseTracker;
107
+ constructor(context, phaseTracker) {
108
+ super(context);
109
+ this.phaseTracker = phaseTracker;
110
+ }
111
+ render() {
112
+ const phase = this.phaseTracker.getPhase();
113
+ if (phase === null) return null;
114
+ return phase !== "idle" ? yellow("Plan") : green("Build");
115
+ }
116
+ };
117
+ //#endregion
118
+ //#region src/modules/model.module.ts
119
+ var ModelModule = class extends Module {
120
+ render() {
121
+ const model = this.context.model;
122
+ if (!model) return null;
123
+ const provider = formatProviderName(model.provider);
124
+ const thinkingLevel = this.context.getThinkingLevel();
125
+ const parenthesisContent = thinkingLevel !== "off" ? `${provider}${dim("/")}${cyan(capitalizeFirst(thinkingLevel))}` : provider;
126
+ return `${bold(magenta(model.name))}${dim(" (")}${parenthesisContent}${dim(")")}`;
127
+ }
128
+ };
129
+ //#endregion
130
+ //#region src/modules/starship.module.ts
131
+ const execFileAsync = promisify(execFile);
132
+ const STARSHIP_ARGUMENTS = [
133
+ "--status=0",
134
+ "--keymap=",
135
+ "--pipestatus=0",
136
+ "--cmd-duration=0",
137
+ "--jobs=0"
138
+ ];
139
+ const ESCAPE_CHARACTER = "\x1B";
140
+ const TRAILING_SGR_PATTERN = new RegExp(`${ESCAPE_CHARACTER}\\[[0-9;]*m+$`, "g");
141
+ var StarshipModule = class extends Module {
142
+ prompt = null;
143
+ async refresh() {
144
+ this.prompt = await this.fetchStarshipPrompt();
145
+ }
146
+ render() {
147
+ return this.prompt;
148
+ }
149
+ async fetchStarshipPrompt() {
150
+ try {
151
+ const { stdout } = await execFileAsync("starship", [
152
+ "prompt",
153
+ `--terminal-width=${this.context.getWidth()}`,
154
+ ...STARSHIP_ARGUMENTS
155
+ ], {
156
+ cwd: this.context.cwd,
157
+ timeout: 3e3,
158
+ env: {
159
+ ...process.env,
160
+ PWD: this.context.cwd,
161
+ STARSHIP_SHELL: "bash"
162
+ }
163
+ });
164
+ return this.cleanStarshipOutput(stdout);
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+ cleanStarshipOutput(stdout) {
170
+ return (stdout.split("\n")[0] ?? "").replace(/\\\[/g, "").replace(/\\\]/g, "").replace(/%\{/g, ESCAPE_CHARACTER).replace(/%\}/g, "").replace(TRAILING_SGR_PATTERN, "").trimEnd() || null;
171
+ }
172
+ };
173
+ //#endregion
174
+ //#region src/modules/tokens.module.ts
175
+ var TokensModule = class extends Module {
176
+ render() {
177
+ const { inputTokens, outputTokens, totalCost } = this.aggregateUsage(this.context.sessionManager.getBranch());
178
+ if (inputTokens === 0 && outputTokens === 0) return null;
179
+ return [
180
+ cyan(`↑${formatTokenCount(inputTokens)}`),
181
+ green(`↓${formatTokenCount(outputTokens)}`),
182
+ yellow(`$${totalCost.toFixed(3)}`)
183
+ ].join(dim("/"));
184
+ }
185
+ aggregateUsage(branch) {
186
+ let inputTokens = 0;
187
+ let outputTokens = 0;
188
+ let totalCost = 0;
189
+ for (const entry of branch) {
190
+ if (entry.type !== "message" || entry.message.role !== "assistant") continue;
191
+ const message = entry.message;
192
+ inputTokens += message.usage.input;
193
+ outputTokens += message.usage.output;
194
+ totalCost += message.usage.cost.total;
195
+ }
196
+ return {
197
+ inputTokens,
198
+ outputTokens,
199
+ totalCost
200
+ };
201
+ }
202
+ };
203
+ //#endregion
204
+ //#region src/modules/yolo.module.ts
205
+ /** Published by @gotgenes/pi-permission-system via ctx.ui.setStatus(). */
206
+ const PERMISSION_SYSTEM_STATUS_KEY = "pi-permission-system";
207
+ const PERMISSION_SYSTEM_YOLO_STATUS_VALUE = "yolo";
208
+ var YoloModule = class extends Module {
209
+ render() {
210
+ if (this.getStatuses().get(PERMISSION_SYSTEM_STATUS_KEY) !== PERMISSION_SYSTEM_YOLO_STATUS_VALUE) return null;
211
+ return bold(rainbow("Yolo"));
212
+ }
213
+ };
214
+ //#endregion
215
+ //#region src/index.ts
216
+ const MODULE_SEPARATOR = dim(" ⋅ ");
217
+ const HORIZONTAL_PADDING = " ";
218
+ function StarshipExtension(pi) {
219
+ let thinkingLevel = "off";
220
+ let lastRenderWidth = 120;
221
+ let requestRender;
222
+ let refreshAll;
223
+ let extensionStatuses = /* @__PURE__ */ new Map();
224
+ const phaseTracker = createPhaseTracker(pi, () => requestRender?.());
225
+ pi.on("session_start", async (_, context) => {
226
+ thinkingLevel = pi.getThinkingLevel();
227
+ const moduleContext = {
228
+ ...context,
229
+ getStatuses: () => extensionStatuses,
230
+ getThinkingLevel: () => thinkingLevel,
231
+ getWidth: () => lastRenderWidth
232
+ };
233
+ const leftModules = [new StarshipModule(moduleContext), new McpModule(moduleContext)];
234
+ const rightModules = [
235
+ new YoloModule(moduleContext),
236
+ new ModeModule(moduleContext, phaseTracker),
237
+ new ModelModule(moduleContext),
238
+ new TokensModule(moduleContext)
239
+ ];
240
+ const allModules = [...leftModules, ...rightModules];
241
+ refreshAll = async () => {
242
+ await Promise.all(allModules.map((item) => item.refresh()));
243
+ requestRender?.();
244
+ };
245
+ await refreshAll();
246
+ context.ui.setFooter((tui, _, data) => {
247
+ requestRender = () => tui.requestRender();
248
+ return {
249
+ dispose: data.onBranchChange(() => refreshAll?.()),
250
+ invalidate() {},
251
+ render(width) {
252
+ extensionStatuses = data.getExtensionStatuses();
253
+ phaseTracker.query();
254
+ if (width !== lastRenderWidth) {
255
+ lastRenderWidth = width;
256
+ for (const item of leftModules) item.refresh();
257
+ }
258
+ const contentWidth = width - 2;
259
+ const left = leftModules.map((item) => item.render()).filter((value) => value !== null).join(MODULE_SEPARATOR);
260
+ const right = rightModules.map((item) => item.render()).filter((value) => value !== null).join(MODULE_SEPARATOR);
261
+ return [HORIZONTAL_PADDING + truncateToWidth(left + " ".repeat(Math.max(1, contentWidth - visibleWidth(left) - visibleWidth(right))) + right, contentWidth) + HORIZONTAL_PADDING];
262
+ }
263
+ };
264
+ });
265
+ });
266
+ pi.on("agent_end", () => {
267
+ refreshAll?.();
268
+ });
269
+ pi.on("thinking_level_select", (event, _) => {
270
+ thinkingLevel = event.level;
271
+ requestRender?.();
272
+ });
273
+ }
274
+ //#endregion
275
+ export { StarshipExtension as default };
276
+
277
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../pi-utilities/dist/index.mjs","../src/utilities/types.ts","../src/modules/mcp.module.ts","../src/modules/mode.module.ts","../src/modules/model.module.ts","../src/modules/starship.module.ts","../src/modules/tokens.module.ts","../src/modules/yolo.module.ts","../src/index.ts"],"sourcesContent":["//#region src/color.ts\nconst bold = (text) => `\\x1B[1m${text}\\x1B[22m`;\nconst cyan = (text) => `\\x1B[96m${text}\\x1B[39m`;\nconst dim = (text) => `\\x1B[2m${text}\\x1B[22m`;\nconst green = (text) => `\\x1B[92m${text}\\x1B[39m`;\nconst magenta = (text) => `\\x1B[95m${text}\\x1B[39m`;\nconst yellow = (text) => `\\x1B[93m${text}\\x1B[39m`;\nfunction hyperlink(url, text) {\n\treturn `\\x1B]8;;${url}\\x1B\\\\${text}\\x1B]8;;\\x1B\\\\`;\n}\nconst RAINBOW_COLORS = [\n\tmagenta,\n\tyellow,\n\tgreen,\n\tcyan\n];\nfunction rainbow(text) {\n\treturn [...text].map((character, index) => RAINBOW_COLORS[index % RAINBOW_COLORS.length](character)).join(\"\");\n}\n//#endregion\n//#region src/format.ts\nconst ANSI_SGR_PATTERN = new RegExp(`\u001b\\\\[[0-9;]*m`, \"g\");\nconst PROVIDER_NAME_OVERRIDES = {\n\t\"amazon\": \"Amazon\",\n\t\"anthropic\": \"Anthropic\",\n\t\"azure\": \"Azure\",\n\t\"cloudflare\": \"Cloudflare\",\n\t\"google\": \"Google\",\n\t\"mistral\": \"Mistral\",\n\t\"openai\": \"OpenAI\",\n\t\"opencode-go\": \"Opencode Go\",\n\t\"opencode\": \"Opencode\",\n\t\"openrouter\": \"OpenRouter\"\n};\nfunction capitalizeFirst(text) {\n\tif (!text) return text;\n\treturn text.charAt(0).toUpperCase() + text.slice(1);\n}\nfunction formatProviderName(provider) {\n\tif (!provider) return provider;\n\tconst override = PROVIDER_NAME_OVERRIDES[provider];\n\tif (override) return override;\n\treturn provider.charAt(0).toUpperCase() + provider.slice(1);\n}\nfunction formatTokenCount(count) {\n\tif (count < 1e3) return `${count}`;\n\treturn `${(count / 1e3).toFixed(1)}k`;\n}\nfunction stripAnsi(text) {\n\treturn text.replace(ANSI_SGR_PATTERN, \"\");\n}\n//#endregion\nexport { bold, capitalizeFirst, cyan, dim, formatProviderName, formatTokenCount, green, hyperlink, magenta, rainbow, stripAnsi, yellow };\n\n//# sourceMappingURL=index.mjs.map","import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\n\nexport type ModuleContext = {\n\tgetStatuses: () => ReadonlyMap<string, string>;\n\tgetThinkingLevel: () => string;\n\tgetWidth: () => number;\n} & ExtensionContext;\n\nexport abstract class Module {\n\tpublic constructor(\n\t\tprotected readonly context: ModuleContext,\n\t) {}\n\n\tprotected getStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.context.getStatuses();\n\t}\n\n\tpublic abstract render(): string | null;\n\n\tpublic async refresh(): Promise<void> {}\n}\n","import { cyan, dim } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\n/** Status key published by pi-mcp-adapter (e.g. \"MCP: 2/3 servers\"). */\nconst MCP_STATUS_KEY = \"mcp\";\nconst SERVER_RATIO_PATTERN = /(\\d+)\\/(\\d+)/;\n\nexport class McpModule extends Module {\n\tpublic override render(): string | null {\n\t\tconst status = this.getStatuses().get(MCP_STATUS_KEY);\n\n\t\tif (!status)\n\t\t\treturn null;\n\n\t\tconst match = SERVER_RATIO_PATTERN.exec(status);\n\n\t\tif (!match)\n\t\t\treturn null;\n\n\t\treturn `${cyan(\"MCP\")} ${dim(\"(\")}${match[1]}/${match[2]}${dim(\")\")}`;\n\t}\n}\n","import type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\nimport type { ModuleContext } from \"~/utilities/types\";\n\nimport { green, yellow } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\nconst REQUEST_CHANNEL = \"plannotator:request\";\n\ntype PlannotatorPhase = \"idle\" | \"planning\" | \"executing\";\n\nexport type PhaseTracker = {\n\tquery: () => void;\n\tgetPhase: () => PlannotatorPhase | null;\n};\n\nexport function createPhaseTracker(pi: ExtensionAPI, onPhaseChange: () => void): PhaseTracker {\n\tlet currentPhase: PlannotatorPhase | null = null;\n\n\tfunction applyPhaseFromResponse(response: any) {\n\t\tif (response?.status !== \"handled\" || !response.result?.phase)\n\t\t\treturn;\n\t\tif (currentPhase === response.result.phase)\n\t\t\treturn;\n\n\t\tcurrentPhase = response.result.phase;\n\t\tonPhaseChange();\n\t}\n\n\tpi.events.on(REQUEST_CHANNEL, (data: any) => {\n\t\tif (data?.action !== \"plan-mode\" || typeof data?.respond !== \"function\")\n\t\t\treturn;\n\n\t\tconst originalRespond = data.respond;\n\n\t\tdata.respond = (response: any) => {\n\t\t\toriginalRespond(response);\n\t\t\tapplyPhaseFromResponse(response);\n\t\t};\n\t});\n\n\tfunction query() {\n\t\tpi.events.emit(REQUEST_CHANNEL, {\n\t\t\trequestId: Math.random().toString(36).slice(2),\n\t\t\taction: \"plan-mode\",\n\t\t\tpayload: { mode: \"status\" },\n\t\t\trespond: (response: any) => applyPhaseFromResponse(response),\n\t\t});\n\t}\n\n\treturn { query, getPhase: () => currentPhase };\n}\n\nexport class ModeModule extends Module {\n\tpublic constructor(context: ModuleContext, private readonly phaseTracker: PhaseTracker) {\n\t\tsuper(context);\n\t}\n\n\tpublic override render(): string | null {\n\t\tconst phase = this.phaseTracker.getPhase();\n\n\t\tif (phase === null)\n\t\t\treturn null;\n\n\t\treturn phase !== \"idle\" ? yellow(\"Plan\") : green(\"Build\");\n\t}\n}\n","import { bold, capitalizeFirst, cyan, dim, formatProviderName, magenta } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\nexport class ModelModule extends Module {\n\tpublic override render(): string | null {\n\t\tconst model = this.context.model;\n\n\t\tif (!model)\n\t\t\treturn null;\n\n\t\tconst provider = formatProviderName(model.provider);\n\t\tconst thinkingLevel = this.context.getThinkingLevel();\n\n\t\tconst parenthesisContent = thinkingLevel !== \"off\"\n\t\t\t? `${provider}${dim(\"/\")}${cyan(capitalizeFirst(thinkingLevel))}`\n\t\t\t: provider;\n\n\t\treturn `${bold(magenta(model.name))}${dim(\" (\")}${parenthesisContent}${dim(\")\")}`;\n\t}\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { Module } from \"~/utilities/types\";\n\nconst execFileAsync = promisify(execFile);\n\nconst STARSHIP_ARGUMENTS = [\n\t\"--status=0\",\n\t\"--keymap=\",\n\t\"--pipestatus=0\",\n\t\"--cmd-duration=0\",\n\t\"--jobs=0\",\n];\n\nconst ESCAPE_CHARACTER = \"\\x1B\";\nconst TRAILING_SGR_PATTERN = new RegExp(`${ESCAPE_CHARACTER}\\\\[[0-9;]*m+$`, \"g\");\n\nexport class StarshipModule extends Module {\n\tprivate prompt: string | null = null;\n\n\tpublic override async refresh(): Promise<void> {\n\t\tthis.prompt = await this.fetchStarshipPrompt();\n\t}\n\n\tpublic override render(): string | null {\n\t\treturn this.prompt;\n\t}\n\n\tprivate async fetchStarshipPrompt(): Promise<string | null> {\n\t\ttry {\n\t\t\tconst { stdout } = await execFileAsync(\n\t\t\t\t\"starship\",\n\t\t\t\t[\"prompt\", `--terminal-width=${this.context.getWidth()}`, ...STARSHIP_ARGUMENTS],\n\t\t\t\t{\n\t\t\t\t\tcwd: this.context.cwd,\n\t\t\t\t\ttimeout: 3000,\n\t\t\t\t\tenv: { ...process.env, PWD: this.context.cwd, STARSHIP_SHELL: \"bash\" },\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn this.cleanStarshipOutput(stdout);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate cleanStarshipOutput(stdout: string): string | null {\n\t\tconst firstLine = stdout.split(\"\\n\")[0] ?? \"\";\n\n\t\tconst withAnsiEscapes = firstLine\n\t\t\t.replace(/\\\\\\[/g, \"\")\n\t\t\t.replace(/\\\\\\]/g, \"\")\n\t\t\t.replace(/%\\{/g, ESCAPE_CHARACTER)\n\t\t\t.replace(/%\\}/g, \"\");\n\n\t\tconst trimmed = withAnsiEscapes\n\t\t\t.replace(TRAILING_SGR_PATTERN, \"\")\n\t\t\t.trimEnd();\n\n\t\treturn trimmed || null;\n\t}\n}\n","import type { AssistantMessage } from \"@earendil-works/pi-ai\";\n\nimport { cyan, dim, formatTokenCount, green, yellow } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\nexport class TokensModule extends Module {\n\tpublic override render(): string | null {\n\t\tconst { inputTokens, outputTokens, totalCost } = this.aggregateUsage(this.context.sessionManager.getBranch());\n\n\t\tif (inputTokens === 0 && outputTokens === 0)\n\t\t\treturn null;\n\n\t\treturn [\n\t\t\tcyan(`↑${formatTokenCount(inputTokens)}`),\n\t\t\tgreen(`↓${formatTokenCount(outputTokens)}`),\n\t\t\tyellow(`$${totalCost.toFixed(3)}`),\n\t\t].join(dim(\"/\"));\n\t}\n\n\tprivate aggregateUsage(branch: any[]): { inputTokens: number; outputTokens: number; totalCost: number } {\n\t\tlet inputTokens = 0;\n\t\tlet outputTokens = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of branch) {\n\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\")\n\t\t\t\tcontinue;\n\n\t\t\tconst message = entry.message as AssistantMessage;\n\t\t\tinputTokens += message.usage.input;\n\t\t\toutputTokens += message.usage.output;\n\t\t\ttotalCost += message.usage.cost.total;\n\t\t}\n\n\t\treturn { inputTokens, outputTokens, totalCost };\n\t}\n}\n","import { bold, rainbow } from \"@ceo.paludetto/pi-utilities\";\n\nimport { Module } from \"~/utilities/types\";\n\n/** Published by @gotgenes/pi-permission-system via ctx.ui.setStatus(). */\nconst PERMISSION_SYSTEM_STATUS_KEY = \"pi-permission-system\";\nconst PERMISSION_SYSTEM_YOLO_STATUS_VALUE = \"yolo\";\n\nexport class YoloModule extends Module {\n\tpublic override render(): string | null {\n\t\tif (this.getStatuses().get(PERMISSION_SYSTEM_STATUS_KEY) !== PERMISSION_SYSTEM_YOLO_STATUS_VALUE)\n\t\t\treturn null;\n\n\t\treturn bold(rainbow(\"Yolo\"));\n\t}\n}\n","import type { Module, ModuleContext } from \"./utilities/types\";\nimport type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\n\nimport { dim } from \"@ceo.paludetto/pi-utilities\";\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\n\nimport { McpModule } from \"./modules/mcp.module\";\nimport { createPhaseTracker, ModeModule } from \"./modules/mode.module\";\nimport { ModelModule } from \"./modules/model.module\";\nimport { StarshipModule } from \"./modules/starship.module\";\nimport { TokensModule } from \"./modules/tokens.module\";\nimport { YoloModule } from \"./modules/yolo.module\";\n\nconst MODULE_SEPARATOR = dim(\" ⋅ \");\nconst HORIZONTAL_PADDING = \" \";\n\nexport default function StarshipExtension(pi: ExtensionAPI) {\n\tlet thinkingLevel = \"off\";\n\tlet lastRenderWidth = 120;\n\tlet requestRender: (() => void) | undefined;\n\tlet refreshAll: (() => Promise<void>) | undefined;\n\tlet extensionStatuses: ReadonlyMap<string, string> = new Map();\n\n\tconst phaseTracker = createPhaseTracker(pi, () => requestRender?.());\n\n\tpi.on(\"session_start\", async (_, context) => {\n\t\tthinkingLevel = pi.getThinkingLevel();\n\n\t\tconst moduleContext: ModuleContext = {\n\t\t\t...context,\n\t\t\tgetStatuses: () => extensionStatuses,\n\t\t\tgetThinkingLevel: () => thinkingLevel,\n\t\t\tgetWidth: () => lastRenderWidth,\n\t\t};\n\n\t\tconst leftModules: Module[] = [\n\t\t\tnew StarshipModule(moduleContext),\n\t\t\tnew McpModule(moduleContext),\n\t\t];\n\n\t\tconst rightModules: Module[] = [\n\t\t\tnew YoloModule(moduleContext),\n\t\t\tnew ModeModule(moduleContext, phaseTracker),\n\t\t\tnew ModelModule(moduleContext),\n\t\t\tnew TokensModule(moduleContext),\n\t\t];\n\n\t\tconst allModules = [...leftModules, ...rightModules];\n\n\t\trefreshAll = async () => {\n\t\t\tawait Promise.all(allModules.map((item) => item.refresh()));\n\t\t\trequestRender?.();\n\t\t};\n\n\t\tawait refreshAll();\n\n\t\tcontext.ui.setFooter((tui, _, data) => {\n\t\t\trequestRender = () => tui.requestRender();\n\t\t\tconst unsubscribe = data.onBranchChange(() => refreshAll?.());\n\n\t\t\treturn {\n\t\t\t\tdispose: unsubscribe,\n\t\t\t\tinvalidate() {},\n\n\t\t\t\trender(width: number): string[] {\n\t\t\t\t\textensionStatuses = data.getExtensionStatuses();\n\t\t\t\t\tphaseTracker.query();\n\n\t\t\t\t\tif (width !== lastRenderWidth) {\n\t\t\t\t\t\tlastRenderWidth = width;\n\t\t\t\t\t\tfor (const item of leftModules)\n\t\t\t\t\t\t\titem.refresh();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst contentWidth = width - 2;\n\n\t\t\t\t\tconst left = leftModules\n\t\t\t\t\t\t.map((item) => item.render())\n\t\t\t\t\t\t.filter((value): value is string => value !== null)\n\t\t\t\t\t\t.join(MODULE_SEPARATOR);\n\n\t\t\t\t\tconst right = rightModules\n\t\t\t\t\t\t.map((item) => item.render())\n\t\t\t\t\t\t.filter((value): value is string => value !== null)\n\t\t\t\t\t\t.join(MODULE_SEPARATOR);\n\n\t\t\t\t\tconst gap = \" \".repeat(Math.max(1, contentWidth - visibleWidth(left) - visibleWidth(right)));\n\t\t\t\t\treturn [HORIZONTAL_PADDING + truncateToWidth(left + gap + right, contentWidth) + HORIZONTAL_PADDING];\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t});\n\n\tpi.on(\"agent_end\", () => {\n\t\trefreshAll?.();\n\t});\n\n\tpi.on(\"thinking_level_select\", (event, _) => {\n\t\tthinkingLevel = event.level;\n\t\trequestRender?.();\n\t});\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ceo.paludetto/pi-starship",
3
3
  "type": "module",
4
- "version": "0.0.0",
4
+ "version": "0.0.1",
5
5
  "description": "Starship-style status bar for pi with mode, model, tokens and path segments",
6
6
  "pi": {
7
7
  "extensions": [
@@ -46,16 +46,16 @@
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "@gotgenes/pi-permission-system": {
49
- "optional": true
50
- },
51
- "pi-mcp-adapter": {
52
- "optional": true
53
- }
54
- },
55
- "devDependencies": {
56
- "@ceo.paludetto/pi-utilities": "0.0.0",
57
- "@earendil-works/pi-ai": "^0.80.2",
58
- "@earendil-works/pi-coding-agent": "^0.80.2",
59
- "@earendil-works/pi-tui": "^0.80.2"
60
- }
49
+ "optional": true
50
+ },
51
+ "pi-mcp-adapter": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "devDependencies": {
56
+ "@ceo.paludetto/pi-utilities": "0.0.0",
57
+ "@earendil-works/pi-ai": "^0.80.2",
58
+ "@earendil-works/pi-coding-agent": "^0.80.2",
59
+ "@earendil-works/pi-tui": "^0.80.2"
60
+ }
61
61
  }