@janvitos/pi-plan-build 0.1.16 → 0.1.18

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.
Files changed (4) hide show
  1. package/README.md +1 -1
  2. package/index.ts +19 -5
  3. package/package.json +1 -1
  4. package/utils.ts +15 -10
package/README.md CHANGED
@@ -8,7 +8,7 @@ A global [Pi coding agent](https://github.com/badlogic/pi-mono) extension that a
8
8
 
9
9
  - New sessions start in **Build** mode.
10
10
  - Bare `Tab` cycles **Build → Plan → Build**, while active autocomplete dropdowns retain Pi's normal Tab completion.
11
- - The composer uses OpenCode prompt-inspired blue/orange mode colors on the rounded top-left border and left rail, complemented by Pi's border color on the right rail and rounded bottom-right border; the frame leaves open gaps at the top right and bottom left. It also includes a mode/model/thinking metadata row; cycling the thinking level updates this row without adding a duplicate status above the composer. The model is shown as `model-id [provider]` (for example, `gpt-5.6-luna [openai]`), with the model ID inheriting the terminal foreground like unselected entries in `/model` and the provider using the footer's dim text color. The footer keeps the remaining path and usage stats without duplicating model metadata.
11
+ - The composer uses OpenCode prompt-inspired blue/orange mode colors on the rounded top-left border and left rail, complemented by Pi's border color on the right rail and rounded bottom-right border; dim rounded corners with color-matched horizontal `╌` and vertical `┆` segments bridge the borders at the top right and bottom left. It also includes a mode/model/thinking metadata row; cycling the thinking level updates this row without adding a duplicate status above the composer. The model is shown as `model-id [provider]` (for example, `gpt-5.6-luna [openai]`), with the model ID inheriting the terminal foreground like unselected entries in `/model` and the provider using the footer's dim text color. The footer keeps the remaining path and usage stats without duplicating model metadata.
12
12
  - `/plan`, `/build`, and the `--plan` startup flag.
13
13
  - Per-session plans at `~/.pi/agent/plans/<session-id>.md`.
14
14
  - In Plan mode, built-in `edit` and `write` are restricted to the exact plan file.
package/index.ts CHANGED
@@ -495,19 +495,33 @@ export default function planBuildModes(pi: ExtensionAPI): void {
495
495
 
496
496
  const leftRail = `${formatModeRail(selectedMode)} `;
497
497
  const rightRail = this.borderColor("│");
498
+ const topRightVerticalTransition = this.borderColor("┆");
499
+ const bottomLeftVerticalTransition = formatModeRail(selectedMode, "┆");
498
500
  const metadata = truncateToWidth(
499
501
  formatModeMetadata(selectedMode, pi.getThinkingLevel(), ctx.ui.theme, this.borderColor, {
500
502
  modelName: ctx.model?.id ?? "no-model",
501
503
  modelProvider: ctx.model?.provider,
504
+ rail: bottomLeftVerticalTransition,
502
505
  }),
503
506
  width - 1,
504
507
  "",
505
508
  );
506
- const topBorder = formatModeTopBorder(selectedMode, width);
507
- return renderModeComposer(lines, topBorder, leftRail, rightRail, metadata, railWidth, width, {
508
- truncate: (line, maxWidth) => truncateToWidth(line, maxWidth, ""),
509
- measure: visibleWidth,
510
- });
509
+ const topBorder = formatModeTopBorder(selectedMode, width, ctx.ui.theme.fg("dim", "╮"));
510
+ return renderModeComposer(
511
+ lines,
512
+ topBorder,
513
+ leftRail,
514
+ rightRail,
515
+ topRightVerticalTransition,
516
+ metadata,
517
+ ctx.ui.theme.fg("dim", "╰"),
518
+ railWidth,
519
+ width,
520
+ {
521
+ truncate: (line, maxWidth) => truncateToWidth(line, maxWidth, ""),
522
+ measure: visibleWidth,
523
+ },
524
+ );
511
525
  }
512
526
 
513
527
  override handleInput(data: string): void {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@janvitos/pi-plan-build",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Plan safely, approve explicitly, then implement here or in a clean session.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/utils.ts CHANGED
@@ -15,6 +15,7 @@ export interface ModeStatusTheme {
15
15
  export interface PromptMetadataOptions {
16
16
  modelName: string;
17
17
  modelProvider?: string;
18
+ rail?: string;
18
19
  }
19
20
 
20
21
  export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -38,13 +39,13 @@ function formatModeColor(mode: Mode, text: string): string {
38
39
  return `\x1b[${MODE_LABELS[mode].color}m${text}${ANSI_RESET}`;
39
40
  }
40
41
 
41
- export function formatModeRail(mode: Mode): string {
42
- return formatModeColor(mode, "│");
42
+ export function formatModeRail(mode: Mode, glyph = "│"): string {
43
+ return formatModeColor(mode, glyph);
43
44
  }
44
45
 
45
- export function formatModeTopBorder(mode: Mode, width: number): string {
46
- if (width <= 1) return "";
47
- return formatModeColor(mode, `╭${"─".repeat(width - 2)}`);
46
+ export function formatModeTopBorder(mode: Mode, width: number, topRightCorner: string): string {
47
+ if (width <= 2) return "";
48
+ return `${formatModeColor(mode, `╭${"─".repeat(width - 3)}╌`)}${topRightCorner}`;
48
49
  }
49
50
 
50
51
  export function formatModeMetadata(
@@ -62,7 +63,7 @@ export function formatModeMetadata(
62
63
  }`
63
64
  : "";
64
65
  const thinkingSeparator = options ? " • " : " · ";
65
- return `${formatModeRail(mode)} ${modeText}${modelText}${theme.fg("dim", thinkingSeparator)}${thinkingColor(thinkingLevel)}`;
66
+ return `${options?.rail ?? formatModeRail(mode)} ${modeText}${modelText}${theme.fg("dim", thinkingSeparator)}${thinkingColor(thinkingLevel)}`;
66
67
  }
67
68
 
68
69
  export function formatTokens(count: number): string {
@@ -94,7 +95,9 @@ export function renderModeComposer(
94
95
  topBorder: string,
95
96
  leftRailPrefix: string,
96
97
  rightRail: string,
98
+ topRightRail: string,
97
99
  metadata: string,
100
+ bottomLeftCorner: string,
98
101
  reservedWidth: number,
99
102
  width: number,
100
103
  lineWidth: LineWidthTools,
@@ -104,18 +107,20 @@ export function renderModeComposer(
104
107
  const bottomBorderIndex = lines.findIndex((line, index) => index > 0 && !line.startsWith(reservedPrefix));
105
108
  if (bottomBorderIndex < 2) return lines;
106
109
 
107
- const addRightRail = (line: string): string => {
110
+ const addRightRail = (line: string, rail = rightRail): string => {
108
111
  const content = lineWidth.truncate(line, width - 1);
109
- return `${content}${" ".repeat(Math.max(0, width - 1 - lineWidth.measure(content)))}${rightRail}`;
112
+ return `${content}${" ".repeat(Math.max(0, width - 1 - lineWidth.measure(content)))}${rail}`;
110
113
  };
111
114
  const promptLines = lines
112
115
  .slice(1, bottomBorderIndex)
113
116
  .map((line) => addRightRail(leftRailPrefix + line.slice(reservedPrefix.length)));
114
- const bottomBorder = lineWidth.truncate(lines[bottomBorderIndex]!, width)
115
- .replace(/^((?:\x1b\[[0-?]*[ -/]*[@-~])*)./u, "$1 ")
117
+ const ansiSequence = "(?:\\x1b\\[[0-?]*[ -/]*[@-~])*";
118
+ const bottomBorder = bottomLeftCorner + lineWidth.truncate(lines[bottomBorderIndex]!, width)
119
+ .replace(new RegExp(`^(${ansiSequence}).${ansiSequence}.`, "u"), "$1╌")
116
120
  .replace(/.(?=(?:\x1b\[[0-?]*[ -/]*[@-~])*$)/u, "╯");
117
121
  return [
118
122
  topBorder,
123
+ addRightRail(leftRailPrefix, topRightRail),
119
124
  ...promptLines,
120
125
  addRightRail(leftRailPrefix),
121
126
  addRightRail(metadata),