@narumitw/pi-statusline 0.49.2 → 0.49.4

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 CHANGED
@@ -35,11 +35,11 @@ A representative uncolored layout:
35
35
  pi install npm:@narumitw/pi-statusline
36
36
  ```
37
37
 
38
- Try it without installing, or load it from this repository:
38
+ Try the published package without installing, or load it directly from this repository:
39
39
 
40
40
  ```bash
41
41
  pi -e npm:@narumitw/pi-statusline
42
- pi -e ./extensions/pi-statusline
42
+ just try statusline
43
43
  ```
44
44
 
45
45
  For the best result, use a terminal font that includes Powerline glyphs and emoji.
@@ -336,8 +336,9 @@ Put transient activity in the value and always clear the same complete key.
336
336
  ```text
337
337
  extensions/pi-statusline/
338
338
  ├── src/
339
- │ ├── index.ts
340
- │ ├── statusline.ts
339
+ │ ├── index.ts # thin entrypoint forwarding to the source runtime
340
+ │ ├── statusline.ts # authoritative lifecycle implementation
341
+ │ ├── command-contract.ts
341
342
  │ ├── render.ts
342
343
  │ ├── directory.ts
343
344
  │ ├── usage.ts
@@ -357,7 +358,8 @@ extensions/pi-statusline/
357
358
  └── package.json
358
359
  ```
359
360
 
360
- `src/index.ts` is the thin Pi entrypoint; all other modules are package-internal.
361
+ `src/` is the authoritative published implementation, and `src/index.ts` remains the sole declared
362
+ Pi entrypoint.
361
363
 
362
364
  ## 🔎 Keywords
363
365
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-statusline",
3
- "version": "0.49.2",
3
+ "version": "0.49.4",
4
4
  "description": "Pi extension that replaces the footer with an information-rich statusline.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  "@earendil-works/pi-tui": "*"
33
33
  },
34
34
  "devDependencies": {
35
- "@biomejs/biome": "2.5.6",
35
+ "@biomejs/biome": "2.5.7",
36
36
  "@earendil-works/pi-coding-agent": "0.83.0",
37
37
  "@earendil-works/pi-tui": "0.83.0",
38
38
  "@types/node": "26.1.2",
@@ -0,0 +1,13 @@
1
+ import type { AutocompleteItem } from "@earendil-works/pi-tui";
2
+
3
+ export const STATUSLINE_SUBCOMMANDS: readonly AutocompleteItem[] = [
4
+ { value: "settings", label: "settings", description: "Edit pi-statusline.json" },
5
+ { value: "status", label: "status", description: "Show effective statusline settings" },
6
+ { value: "help", label: "help", description: "Show configuration help" },
7
+ ];
8
+
9
+ export function completeStatuslineArguments(prefix: string): AutocompleteItem[] | null {
10
+ const normalized = prefix.trim().toLowerCase();
11
+ const matches = STATUSLINE_SUBCOMMANDS.filter((item) => item.value.startsWith(normalized));
12
+ return matches.length > 0 ? [...matches] : null;
13
+ }
package/src/commands.ts CHANGED
@@ -4,7 +4,6 @@ import {
4
4
  type ExtensionCommandContext,
5
5
  } from "@earendil-works/pi-coding-agent";
6
6
  import {
7
- type AutocompleteItem,
8
7
  Container,
9
8
  Key,
10
9
  matchesKey,
@@ -14,6 +13,7 @@ import {
14
13
  truncateToWidth,
15
14
  wrapTextWithAnsi,
16
15
  } from "@earendil-works/pi-tui";
16
+ import { completeStatuslineArguments } from "./command-contract.js";
17
17
  import {
18
18
  INFORMATION_PROFILE_NAMES,
19
19
  INFORMATION_PROFILES,
@@ -56,12 +56,6 @@ const SEGMENT_DESCRIPTIONS: Record<SegmentName, string> = {
56
56
  time: "Current local time",
57
57
  turn: "Current session turn count",
58
58
  };
59
- const SUBCOMMANDS: AutocompleteItem[] = [
60
- { value: "settings", label: "settings", description: "Edit pi-statusline.json" },
61
- { value: "status", label: "status", description: "Show effective statusline settings" },
62
- { value: "help", label: "help", description: "Show configuration help" },
63
- ];
64
-
65
59
  export interface StatuslineCommandOptions {
66
60
  settingsPath: string;
67
61
  getLoaded(): LoadedStatuslineSettings;
@@ -74,44 +68,46 @@ export interface StatuslineCommandOptions {
74
68
  export function registerStatuslineCommand(pi: ExtensionAPI, options: StatuslineCommandOptions) {
75
69
  pi.registerCommand("statusline", {
76
70
  description: "Open or inspect the statusline settings",
77
- getArgumentCompletions(prefix: string): AutocompleteItem[] | null {
78
- const normalized = prefix.trim().toLowerCase();
79
- const matches = SUBCOMMANDS.filter((item) => item.value.startsWith(normalized));
80
- return matches.length > 0 ? matches : null;
81
- },
82
- handler: async (args, ctx) => {
83
- const normalized = args.trim();
84
- if (!normalized) {
85
- await showMainMenu(ctx, options);
86
- return;
87
- }
71
+ getArgumentCompletions: completeStatuslineArguments,
72
+ handler: (args, ctx) => handleStatuslineCommand(args, ctx, options),
73
+ });
74
+ }
88
75
 
89
- const tokens = normalized.split(/\s+/u);
90
- const subcommand = tokens[0]?.toLowerCase() ?? "";
91
- if (tokens.length > 1) {
92
- if (canNotify(ctx)) {
93
- ctx.ui.notify(`/statusline ${subcommand} does not accept trailing arguments.`, "warning");
94
- }
95
- return;
96
- }
76
+ export async function handleStatuslineCommand(
77
+ args: string,
78
+ ctx: ExtensionCommandContext,
79
+ options: StatuslineCommandOptions,
80
+ ) {
81
+ const normalized = args.trim();
82
+ if (!normalized) {
83
+ await showMainMenu(ctx, options);
84
+ return;
85
+ }
97
86
 
98
- switch (subcommand) {
99
- case "settings":
100
- await editSettings(ctx, options);
101
- return;
102
- case "status":
103
- showStatus(ctx, options);
104
- return;
105
- case "help":
106
- showHelp(ctx, options.settingsPath);
107
- return;
108
- default:
109
- if (canNotify(ctx)) {
110
- ctx.ui.notify(`Unknown /statusline subcommand: ${subcommand}`, "warning");
111
- }
87
+ const tokens = normalized.split(/\s+/u);
88
+ const subcommand = tokens[0]?.toLowerCase() ?? "";
89
+ if (tokens.length > 1) {
90
+ if (canNotify(ctx)) {
91
+ ctx.ui.notify(`/statusline ${subcommand} does not accept trailing arguments.`, "warning");
92
+ }
93
+ return;
94
+ }
95
+
96
+ switch (subcommand) {
97
+ case "settings":
98
+ await editSettings(ctx, options);
99
+ return;
100
+ case "status":
101
+ showStatus(ctx, options);
102
+ return;
103
+ case "help":
104
+ showHelp(ctx, options.settingsPath);
105
+ return;
106
+ default:
107
+ if (canNotify(ctx)) {
108
+ ctx.ui.notify(`Unknown /statusline subcommand: ${subcommand}`, "warning");
112
109
  }
113
- },
114
- });
110
+ }
115
111
  }
116
112
 
117
113
  async function showMainMenu(ctx: ExtensionCommandContext, options: StatuslineCommandOptions) {
package/src/statusline.ts CHANGED
@@ -4,7 +4,8 @@ import {
4
4
  type ExtensionContext,
5
5
  getAgentDir,
6
6
  } from "@earendil-works/pi-coding-agent";
7
- import { registerStatuslineCommand } from "./commands.js";
7
+ import { completeStatuslineArguments } from "./command-contract.js";
8
+ import type { StatuslineCommandOptions } from "./commands.js";
8
9
  import {
9
10
  buildExtensionStatusIconAliases,
10
11
  type ExtensionStatusIconAliasMap,
@@ -27,6 +28,19 @@ const GIT_STATUS_REFRESH_INTERVAL_MS = 30_000;
27
28
  const GIT_STATUS_EVENT_DEBOUNCE_MS = 250;
28
29
  const EMPTY_EXTENSION_STATUS_ICON_ALIASES: ExtensionStatusIconAliasMap = new Map();
29
30
 
31
+ type StatuslineCommands = typeof import("./commands.js");
32
+ let statuslineCommandsPromise: Promise<StatuslineCommands> | undefined;
33
+
34
+ function loadStatuslineCommands(): Promise<StatuslineCommands> {
35
+ if (!statuslineCommandsPromise) {
36
+ statuslineCommandsPromise = import("./commands.js").catch((error) => {
37
+ statuslineCommandsPromise = undefined;
38
+ throw error;
39
+ });
40
+ }
41
+ return statuslineCommandsPromise;
42
+ }
43
+
30
44
  export default function statusline(pi: ExtensionAPI) {
31
45
  let loaded: LoadedStatuslineSettings | undefined;
32
46
  let previewPalettePreset: PalettePreset | undefined;
@@ -193,7 +207,7 @@ export default function statusline(pi: ExtensionAPI) {
193
207
 
194
208
  const agentDir = getAgentDir();
195
209
  const configPath = settingsFilePath(agentDir);
196
- registerStatuslineCommand(pi, {
210
+ const commandOptions: StatuslineCommandOptions = {
197
211
  settingsPath: configPath,
198
212
  getLoaded: () => loaded ?? loadStatuslineSettings(configPath),
199
213
  getMenuOwner: () => {
@@ -214,6 +228,15 @@ export default function statusline(pi: ExtensionAPI) {
214
228
  previewPalettePreset = palettePreset;
215
229
  refresh();
216
230
  },
231
+ };
232
+ pi.registerCommand("statusline", {
233
+ description: "Open or inspect the statusline settings",
234
+ getArgumentCompletions: completeStatuslineArguments,
235
+ handler: async (args, ctx) => {
236
+ const commands = await loadStatuslineCommands();
237
+ if (ctx.sessionManager !== activeSessionManager) return;
238
+ await commands.handleStatuslineCommand(args, ctx, commandOptions);
239
+ },
217
240
  });
218
241
 
219
242
  pi.on("session_start", (_event, ctx) => {