@quandev104/pi-style 0.2.6 → 0.2.9
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/CHANGELOG.md +13 -0
- package/README.md +2 -2
- package/dist/extensions/pi-style.js +200 -31
- package/dist/extensions/pi-style.js.map +1 -1
- package/extension-src/pi-style/features/messages/index.ts +15 -2
- package/extension-src/pi-style/features/startup/index.ts +24 -4
- package/extension-src/pi-style/features/startup/logo.ts +22 -18
- package/extension-src/pi-style/pi/compatibility-probe.ts +162 -16
- package/extension-src/pi-style/shared/ansi.ts +45 -0
- package/package.json +10 -9
|
@@ -567,9 +567,21 @@ function isSpacerChild(child: unknown): boolean {
|
|
|
567
567
|
* rendered content is empty. Duck-typed on the public shape because
|
|
568
568
|
* pi-coding-agent may resolve its own nested pi-tui copy, so `instanceof`
|
|
569
569
|
* across that module boundary is unreliable.
|
|
570
|
+
*
|
|
571
|
+
* Pi 0.85.0 wraps every thinking-run component (the hidden label Text or the
|
|
572
|
+
* visible thinking Markdown) in a `MouseRegion` for click-to-toggle visibility.
|
|
573
|
+
* `MouseRegion` is render-transparent, so the placeholder check unwraps it first
|
|
574
|
+
* (duck-typed: a `handleMouse` function plus a `child`), keeping the same
|
|
575
|
+
* Text detection for the pre-0.85 bare-Text layout.
|
|
570
576
|
*/
|
|
577
|
+
function unwrapMouseRegion(child: unknown): unknown {
|
|
578
|
+
const candidate = child as { handleMouse?: unknown; child?: unknown } | undefined;
|
|
579
|
+
if (typeof candidate?.handleMouse !== "function" || candidate.child === undefined) return child;
|
|
580
|
+
return candidate.child;
|
|
581
|
+
}
|
|
582
|
+
|
|
571
583
|
function isBlankTextChild(child: unknown): boolean {
|
|
572
|
-
const candidate = child as
|
|
584
|
+
const candidate = unwrapMouseRegion(child) as
|
|
573
585
|
| { setCustomBgFn?: unknown; render?: (width: number) => string[]; text?: unknown }
|
|
574
586
|
| undefined;
|
|
575
587
|
if (typeof candidate?.setCustomBgFn !== "function" || typeof candidate.render !== "function") return false;
|
|
@@ -605,7 +617,8 @@ function markChildrenScanned(instance: object, children: readonly unknown[]): vo
|
|
|
605
617
|
*
|
|
606
618
|
* Native `AssistantMessageComponent.updateContent` renders the thinking block as
|
|
607
619
|
* `Text(theme.italic(theme.fg("thinkingText", label)), outputPad, 0)` plus a
|
|
608
|
-
* trailing `Spacer(1)
|
|
620
|
+
* trailing `Spacer(1)` — wrapped in a render-transparent `MouseRegion` since
|
|
621
|
+
* Pi 0.85.0. An empty label is still wrapped in ANSI SGR codes, so
|
|
609
622
|
* `Text.render` cannot treat it as empty (its check is `text.trim() === ""`,
|
|
610
623
|
* and trim does not strip escape sequences) and emits one full-width invisible
|
|
611
624
|
* line. That invisible row plus the surrounding spacers is the "gap" left when
|
|
@@ -2,8 +2,9 @@ import type { Component, OverlayHandle, OverlayOptions } from "@earendil-works/p
|
|
|
2
2
|
import type { NormalizedPiStyleConfig } from "../../domain/config-types.js";
|
|
3
3
|
import type { StatusSnapshot } from "../../domain/status.js";
|
|
4
4
|
import { type ActiveTheme, type ResolvedTheme, resolveTheme } from "../../domain/theme.js";
|
|
5
|
-
import { fitAnsiWidth, truncateAnsi, visibleWidth } from "../../shared/ansi.js";
|
|
6
|
-
import {
|
|
5
|
+
import { fitAnsiWidth, fitAnsiWidthTail, truncateAnsi, visibleWidth } from "../../shared/ansi.js";
|
|
6
|
+
import { shortenPath } from "../../shared/box.js";
|
|
7
|
+
import { compactLogoHeader, logoDetailWidth } from "./logo.js";
|
|
7
8
|
|
|
8
9
|
export type StartupReason = "startup" | "reload" | "new" | "resume" | "fork";
|
|
9
10
|
|
|
@@ -302,6 +303,16 @@ const STARTUP_PADDING_TOP = 2;
|
|
|
302
303
|
/** Blank rows below the block, separating it from the editor / chat. */
|
|
303
304
|
const STARTUP_PADDING_BOTTOM = 2;
|
|
304
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Startup heading title: the working directory of the session (home-contracted
|
|
308
|
+
* to `~`) — i.e. the path of the repo currently being worked in. Falls back to
|
|
309
|
+
* the directory basename, then the brand, when the runtime reports no cwd.
|
|
310
|
+
*/
|
|
311
|
+
function startupProjectTitle(snapshot: StartupSnapshot): string {
|
|
312
|
+
if (snapshot.cwd) return shortenPath(snapshot.cwd);
|
|
313
|
+
return snapshot.project ?? "pi-style";
|
|
314
|
+
}
|
|
315
|
+
|
|
305
316
|
function styledLines(
|
|
306
317
|
theme: ActiveTheme,
|
|
307
318
|
config: NormalizedPiStyleConfig,
|
|
@@ -319,13 +330,22 @@ function styledLines(
|
|
|
319
330
|
// Breathing room above the block (separates it from the status line / terminal top).
|
|
320
331
|
lines.push(...Array.from({ length: STARTUP_PADDING_TOP }, () => ""));
|
|
321
332
|
|
|
322
|
-
|
|
333
|
+
// Heading: the π glyph followed by the current project path (never the
|
|
334
|
+
// package name — the heading identifies WHERE you are, not what styles it).
|
|
335
|
+
// The path is tail-fitted so the repo name survives narrow terminals.
|
|
336
|
+
const asciiMode = resolved.mode === "ascii";
|
|
337
|
+
const glyph = resolved.glyph("pi");
|
|
338
|
+
const projectTitle = startupProjectTitle(snapshot);
|
|
339
|
+
const titleBudget = Math.max(0, logoDetailWidth(bodyWidth) - (asciiMode ? 0 : visibleWidth(glyph) + 1));
|
|
340
|
+
const fittedTitle = fitAnsiWidthTail(projectTitle, titleBudget, asciiMode ? "..." : "…");
|
|
341
|
+
const logoTitle = asciiMode ? fittedTitle : `${glyph} ${fittedTitle}`;
|
|
323
342
|
lines.push(
|
|
324
343
|
...compactLogoHeader(
|
|
325
344
|
resolved,
|
|
326
345
|
[
|
|
327
346
|
resolved.apply("accent", logoTitle),
|
|
328
|
-
resolved.apply("muted", "/ commands
|
|
347
|
+
resolved.apply("muted", "/ commands"),
|
|
348
|
+
resolved.apply("muted", "! bash"),
|
|
329
349
|
resolved.apply("success", "● ready"),
|
|
330
350
|
],
|
|
331
351
|
bodyWidth,
|
|
@@ -9,15 +9,14 @@ import { fitAnsiWidth, parseAnsiFgToRgb, visibleWidth } from "../../shared/ansi.
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export const PI_LOGO_LINES = [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"╚═══╝ ╚═══╝",
|
|
12
|
+
"████████████",
|
|
13
|
+
"████████████",
|
|
14
|
+
"████ ████",
|
|
15
|
+
"████ ████",
|
|
16
|
+
"████████ ████",
|
|
17
|
+
"████████ ████",
|
|
18
|
+
"████ ████",
|
|
19
|
+
"████ ████",
|
|
21
20
|
] as const;
|
|
22
21
|
|
|
23
22
|
const LOGO_PALETTE_STEPS = 24;
|
|
@@ -117,20 +116,25 @@ export function styledLogoLines(resolved: ResolvedTheme): string[] {
|
|
|
117
116
|
return logoGradientCacheLines;
|
|
118
117
|
}
|
|
119
118
|
|
|
119
|
+
/** Width of the side-detail column next to the block-art logo at a given content width. */
|
|
120
|
+
export function logoDetailWidth(width: number): number {
|
|
121
|
+
const logoWidth = Math.max(...PI_LOGO_LINES.map((line) => visibleWidth(line)));
|
|
122
|
+
return Math.max(0, width - logoWidth - visibleWidth(LOGO_GAP));
|
|
123
|
+
}
|
|
124
|
+
|
|
120
125
|
/**
|
|
121
126
|
* Assemble the compact startup header: gradient logo with side details when
|
|
122
127
|
* wide enough, stacked logo + details next, and a minimal title/status pair
|
|
123
|
-
* for very narrow terminals.
|
|
128
|
+
* for very narrow terminals. `details` is a column of detail rows — the first
|
|
129
|
+
* is the title, the last is the status, and the rows between are hints — each
|
|
130
|
+
* rendered on its own line, vertically centered beside the logo. Every
|
|
131
|
+
* returned line fits within `width`.
|
|
124
132
|
*/
|
|
125
|
-
export function compactLogoHeader(
|
|
126
|
-
resolved: ResolvedTheme,
|
|
127
|
-
details: readonly [title: string, hints: string, status: string],
|
|
128
|
-
width: number,
|
|
129
|
-
): string[] {
|
|
133
|
+
export function compactLogoHeader(resolved: ResolvedTheme, details: readonly string[], width: number): string[] {
|
|
130
134
|
const logoLines = styledLogoLines(resolved);
|
|
131
|
-
const logoWidth = Math.max(...PI_LOGO_LINES.map((line) => visibleWidth(line)));
|
|
132
135
|
const safeWidth = Math.max(1, width);
|
|
133
|
-
const
|
|
136
|
+
const logoWidth = Math.max(...PI_LOGO_LINES.map((line) => visibleWidth(line)));
|
|
137
|
+
const detailWidth = logoDetailWidth(safeWidth);
|
|
134
138
|
|
|
135
139
|
if (detailWidth >= LOGO_SIDE_DETAIL_MIN_WIDTH) {
|
|
136
140
|
const detailStartRow = Math.max(0, Math.floor((PI_LOGO_LINES.length - details.length) / 2));
|
|
@@ -147,5 +151,5 @@ export function compactLogoHeader(
|
|
|
147
151
|
return [...logoLines, ...details.map((detail) => fitAnsiWidth(detail, safeWidth))];
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
return [details[0], details[
|
|
154
|
+
return [details[0] ?? "", details[details.length - 1] ?? ""].map((detail) => fitAnsiWidth(detail, safeWidth));
|
|
151
155
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import * as piCodingAgentPackage from "@earendil-works/pi-coding-agent";
|
|
4
5
|
import {
|
|
5
6
|
AssistantMessageComponent,
|
|
6
7
|
BashExecutionComponent,
|
|
@@ -34,8 +35,16 @@ import {
|
|
|
34
35
|
* changes the identity degrades that single surface to its native fallback while
|
|
35
36
|
* every other surface continues.
|
|
36
37
|
*/
|
|
37
|
-
export const SUPPORTED_VERSION_RANGE = ">=0.83.0 <0.
|
|
38
|
-
export const SUPPORTED_PI_VERSIONS: readonly string[] = Object.freeze([
|
|
38
|
+
export const SUPPORTED_VERSION_RANGE = ">=0.83.0 <0.86.0";
|
|
39
|
+
export const SUPPORTED_PI_VERSIONS: readonly string[] = Object.freeze([
|
|
40
|
+
"0.83.0",
|
|
41
|
+
"0.84.0",
|
|
42
|
+
"0.84.1",
|
|
43
|
+
"0.84.2",
|
|
44
|
+
"0.84.3",
|
|
45
|
+
"0.84.4",
|
|
46
|
+
"0.85.0",
|
|
47
|
+
]);
|
|
39
48
|
|
|
40
49
|
/** A recorded native identity for one certified surface. */
|
|
41
50
|
export interface KnownNativeIdentity {
|
|
@@ -53,6 +62,28 @@ export interface KnownNativeIdentity {
|
|
|
53
62
|
* A matching name/arity/hash is evidence of the shipped native method, not a
|
|
54
63
|
* cryptographic proof: code loaded before this module could spoof the same
|
|
55
64
|
* function source. Unrecorded identities therefore fall back natively per surface.
|
|
65
|
+
*
|
|
66
|
+
* Identities are recorded per ARTIFACT FAMILY, not just per version: through Pi
|
|
67
|
+
* 0.84.2 the Node CLI ran the modular `dist/` build and extensions received the
|
|
68
|
+
* pretty-printed dist classes; 0.84.3 switches the CLI entrypoint to a minified
|
|
69
|
+
* bundled runtime (`dist/bundle/chunks/*`, see Pi 0.84.3 CHANGELOG "load a bundled
|
|
70
|
+
* runtime") whose jiti virtual-module map hands extensions the in-bundle class
|
|
71
|
+
* objects. Minification rewrites every method's `Function.prototype.toString()`
|
|
72
|
+
* text while behavior stays identical, so each surface carries BOTH the modular
|
|
73
|
+
* identity (0.83.0–0.84.2) and the bundled identity (0.84.3). The modular
|
|
74
|
+
* `dist/index.js` itself is unchanged in 0.84.3 — the switch is which class
|
|
75
|
+
* objects the running CLI actually serves to extensions.
|
|
76
|
+
*
|
|
77
|
+
* 0.84.4 rebuilds both artifact families byte-identically for every surface
|
|
78
|
+
* below, so it shares the 0.84.3 identities. 0.85.0 drifts exactly three
|
|
79
|
+
* surfaces (in both families) while preserving name/arity and the adapter
|
|
80
|
+
* contracts, so each carries a re-recorded 0.85.0 identity:
|
|
81
|
+
* - `AssistantMessageComponent.updateContent` adds per-run thinking visibility
|
|
82
|
+
* overrides (`thinkingVisibilityOverrides` + a `MouseRegion` click toggle per
|
|
83
|
+
* thinking run; the `isStreaming` default parameter and arity 1 are unchanged).
|
|
84
|
+
* - `ToolExecutionComponent.getCallRenderer`/`getResultRenderer` drop the
|
|
85
|
+
* `builtInToolDefinition` fallback branches and simply return
|
|
86
|
+
* `this.toolDefinition?.renderCall`/`renderResult` (still renderer-or-undefined).
|
|
56
87
|
*/
|
|
57
88
|
export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNativeIdentity[]>> = Object.freeze({
|
|
58
89
|
"native-assistant-message:render": Object.freeze([
|
|
@@ -60,7 +91,13 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
60
91
|
name: "render",
|
|
61
92
|
arity: 1,
|
|
62
93
|
fingerprint: "2a39243f",
|
|
63
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
94
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4", "0.85.0"]),
|
|
95
|
+
}),
|
|
96
|
+
Object.freeze({
|
|
97
|
+
name: "render",
|
|
98
|
+
arity: 1,
|
|
99
|
+
fingerprint: "a9be09a3",
|
|
100
|
+
versions: Object.freeze(["0.84.3", "0.84.4", "0.85.0"]),
|
|
64
101
|
}),
|
|
65
102
|
]),
|
|
66
103
|
"native-assistant-message:updateContent": Object.freeze([
|
|
@@ -77,7 +114,32 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
77
114
|
name: "updateContent",
|
|
78
115
|
arity: 1,
|
|
79
116
|
fingerprint: "d2114491",
|
|
80
|
-
versions: Object.freeze(["0.84.0"]),
|
|
117
|
+
versions: Object.freeze(["0.84.0", "0.84.1", "0.84.2", "0.84.4"]),
|
|
118
|
+
}),
|
|
119
|
+
// 0.84.3: the CLI loads a minified bundled runtime and extensions receive
|
|
120
|
+
// the in-bundle class objects, so this method's toString() is the minified
|
|
121
|
+
// text. See the registry docblock (artifact families) — the modular dist
|
|
122
|
+
// build of 0.84.3 still carries the fingerprint above.
|
|
123
|
+
Object.freeze({
|
|
124
|
+
name: "updateContent",
|
|
125
|
+
arity: 1,
|
|
126
|
+
fingerprint: "356b7e83",
|
|
127
|
+
versions: Object.freeze(["0.84.3", "0.84.4"]),
|
|
128
|
+
}),
|
|
129
|
+
// 0.85.0 modular: adds per-run thinking visibility overrides and a
|
|
130
|
+
// MouseRegion click toggle per thinking run (arity stays 1).
|
|
131
|
+
Object.freeze({
|
|
132
|
+
name: "updateContent",
|
|
133
|
+
arity: 1,
|
|
134
|
+
fingerprint: "80e338d2",
|
|
135
|
+
versions: Object.freeze(["0.85.0"]),
|
|
136
|
+
}),
|
|
137
|
+
// 0.85.0 bundled: same drift, minified.
|
|
138
|
+
Object.freeze({
|
|
139
|
+
name: "updateContent",
|
|
140
|
+
arity: 1,
|
|
141
|
+
fingerprint: "c3d72f2b",
|
|
142
|
+
versions: Object.freeze(["0.85.0"]),
|
|
81
143
|
}),
|
|
82
144
|
]),
|
|
83
145
|
"native-compaction-message:updateDisplay": Object.freeze([
|
|
@@ -85,7 +147,13 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
85
147
|
name: "updateDisplay",
|
|
86
148
|
arity: 0,
|
|
87
149
|
fingerprint: "f8c44e78",
|
|
88
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
150
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4", "0.85.0"]),
|
|
151
|
+
}),
|
|
152
|
+
Object.freeze({
|
|
153
|
+
name: "updateDisplay",
|
|
154
|
+
arity: 0,
|
|
155
|
+
fingerprint: "5118a51d",
|
|
156
|
+
versions: Object.freeze(["0.84.3", "0.84.4", "0.85.0"]),
|
|
89
157
|
}),
|
|
90
158
|
]),
|
|
91
159
|
"native-branch-message:updateDisplay": Object.freeze([
|
|
@@ -93,7 +161,13 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
93
161
|
name: "updateDisplay",
|
|
94
162
|
arity: 0,
|
|
95
163
|
fingerprint: "415d57b7",
|
|
96
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
164
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4", "0.85.0"]),
|
|
165
|
+
}),
|
|
166
|
+
Object.freeze({
|
|
167
|
+
name: "updateDisplay",
|
|
168
|
+
arity: 0,
|
|
169
|
+
fingerprint: "2185274e",
|
|
170
|
+
versions: Object.freeze(["0.84.3", "0.84.4", "0.85.0"]),
|
|
97
171
|
}),
|
|
98
172
|
]),
|
|
99
173
|
"native-skill-message:updateDisplay": Object.freeze([
|
|
@@ -101,7 +175,13 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
101
175
|
name: "updateDisplay",
|
|
102
176
|
arity: 0,
|
|
103
177
|
fingerprint: "48099ea6",
|
|
104
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
178
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4", "0.85.0"]),
|
|
179
|
+
}),
|
|
180
|
+
Object.freeze({
|
|
181
|
+
name: "updateDisplay",
|
|
182
|
+
arity: 0,
|
|
183
|
+
fingerprint: "4051fd65",
|
|
184
|
+
versions: Object.freeze(["0.84.3", "0.84.4", "0.85.0"]),
|
|
105
185
|
}),
|
|
106
186
|
]),
|
|
107
187
|
"native-custom-message:rebuild": Object.freeze([
|
|
@@ -109,7 +189,13 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
109
189
|
name: "rebuild",
|
|
110
190
|
arity: 0,
|
|
111
191
|
fingerprint: "76ae2e3a",
|
|
112
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
192
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4", "0.85.0"]),
|
|
193
|
+
}),
|
|
194
|
+
Object.freeze({
|
|
195
|
+
name: "rebuild",
|
|
196
|
+
arity: 0,
|
|
197
|
+
fingerprint: "b89987cc",
|
|
198
|
+
versions: Object.freeze(["0.84.3", "0.84.4", "0.85.0"]),
|
|
113
199
|
}),
|
|
114
200
|
]),
|
|
115
201
|
"tool-call-renderer:getCallRenderer": Object.freeze([
|
|
@@ -117,7 +203,28 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
117
203
|
name: "getCallRenderer",
|
|
118
204
|
arity: 0,
|
|
119
205
|
fingerprint: "951ea0e0",
|
|
120
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
206
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4"]),
|
|
207
|
+
}),
|
|
208
|
+
Object.freeze({
|
|
209
|
+
name: "getCallRenderer",
|
|
210
|
+
arity: 0,
|
|
211
|
+
fingerprint: "e50613b7",
|
|
212
|
+
versions: Object.freeze(["0.84.3", "0.84.4"]),
|
|
213
|
+
}),
|
|
214
|
+
// 0.85.0 modular: drops the `builtInToolDefinition` fallback branches
|
|
215
|
+
// and returns `this.toolDefinition?.renderCall` directly.
|
|
216
|
+
Object.freeze({
|
|
217
|
+
name: "getCallRenderer",
|
|
218
|
+
arity: 0,
|
|
219
|
+
fingerprint: "e0a9ed86",
|
|
220
|
+
versions: Object.freeze(["0.85.0"]),
|
|
221
|
+
}),
|
|
222
|
+
// 0.85.0 bundled: same drift, minified.
|
|
223
|
+
Object.freeze({
|
|
224
|
+
name: "getCallRenderer",
|
|
225
|
+
arity: 0,
|
|
226
|
+
fingerprint: "73116365",
|
|
227
|
+
versions: Object.freeze(["0.85.0"]),
|
|
121
228
|
}),
|
|
122
229
|
]),
|
|
123
230
|
"tool-result-renderer:getResultRenderer": Object.freeze([
|
|
@@ -125,7 +232,28 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
125
232
|
name: "getResultRenderer",
|
|
126
233
|
arity: 0,
|
|
127
234
|
fingerprint: "8a25cd71",
|
|
128
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
235
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4"]),
|
|
236
|
+
}),
|
|
237
|
+
Object.freeze({
|
|
238
|
+
name: "getResultRenderer",
|
|
239
|
+
arity: 0,
|
|
240
|
+
fingerprint: "28c4dc22",
|
|
241
|
+
versions: Object.freeze(["0.84.3", "0.84.4"]),
|
|
242
|
+
}),
|
|
243
|
+
// 0.85.0 modular: drops the `builtInToolDefinition` fallback branches
|
|
244
|
+
// and returns `this.toolDefinition?.renderResult` directly.
|
|
245
|
+
Object.freeze({
|
|
246
|
+
name: "getResultRenderer",
|
|
247
|
+
arity: 0,
|
|
248
|
+
fingerprint: "1567dcf4",
|
|
249
|
+
versions: Object.freeze(["0.85.0"]),
|
|
250
|
+
}),
|
|
251
|
+
// 0.85.0 bundled: same drift, minified.
|
|
252
|
+
Object.freeze({
|
|
253
|
+
name: "getResultRenderer",
|
|
254
|
+
arity: 0,
|
|
255
|
+
fingerprint: "d613a2a3",
|
|
256
|
+
versions: Object.freeze(["0.85.0"]),
|
|
129
257
|
}),
|
|
130
258
|
]),
|
|
131
259
|
"native-bash-execution:render": Object.freeze([
|
|
@@ -137,7 +265,13 @@ export const KNOWN_NATIVE_IDENTITIES: Readonly<Record<string, readonly KnownNati
|
|
|
137
265
|
name: "BashExecutionComponent",
|
|
138
266
|
arity: 2,
|
|
139
267
|
fingerprint: "a5b5abca",
|
|
140
|
-
versions: Object.freeze(["0.83.0", "0.84.0"]),
|
|
268
|
+
versions: Object.freeze(["0.83.0", "0.84.0", "0.84.1", "0.84.2", "0.84.4", "0.85.0"]),
|
|
269
|
+
}),
|
|
270
|
+
Object.freeze({
|
|
271
|
+
name: "BashExecutionComponent",
|
|
272
|
+
arity: 2,
|
|
273
|
+
fingerprint: "98d22d96",
|
|
274
|
+
versions: Object.freeze(["0.84.3", "0.84.4", "0.85.0"]),
|
|
141
275
|
}),
|
|
142
276
|
]),
|
|
143
277
|
});
|
|
@@ -267,8 +401,19 @@ function trustedNativeIdentity(spec: TargetSpec): unknown {
|
|
|
267
401
|
export interface PiVersionResolution {
|
|
268
402
|
resolvePackageEntry?: (packageName: string) => string;
|
|
269
403
|
readFile?: (path: string) => string;
|
|
404
|
+
/** Test-only override for the runtime VERSION-export fallback source. */
|
|
405
|
+
runtimeVersion?: string;
|
|
270
406
|
}
|
|
271
407
|
|
|
408
|
+
/**
|
|
409
|
+
* The running host build's own VERSION constant. Inside the packaged Pi
|
|
410
|
+
* process extensions are loaded through jiti, where `import.meta.resolve`
|
|
411
|
+
* cannot see the host package at all — this export is aliased to the exact
|
|
412
|
+
* host build and is the authoritative version source there. Read defensively
|
|
413
|
+
* through a namespace import so hosts without the export still load.
|
|
414
|
+
*/
|
|
415
|
+
const RUNTIME_PI_VERSION = (piCodingAgentPackage as { readonly VERSION?: unknown }).VERSION;
|
|
416
|
+
|
|
272
417
|
export function detectPiVersion(resolution: PiVersionResolution = {}): {
|
|
273
418
|
version: string | undefined;
|
|
274
419
|
diagnostic?: string;
|
|
@@ -285,6 +430,7 @@ export function detectPiVersion(resolution: PiVersionResolution = {}): {
|
|
|
285
430
|
}
|
|
286
431
|
});
|
|
287
432
|
const readFile = resolution.readFile ?? ((path: string) => readFileSync(path, "utf8"));
|
|
433
|
+
let walkDiagnostic: string | undefined;
|
|
288
434
|
try {
|
|
289
435
|
const entry = resolvePackageEntry("@earendil-works/pi-coding-agent");
|
|
290
436
|
let directory = dirname(entry);
|
|
@@ -301,13 +447,13 @@ export function detectPiVersion(resolution: PiVersionResolution = {}): {
|
|
|
301
447
|
if (parent === directory) break;
|
|
302
448
|
directory = parent;
|
|
303
449
|
}
|
|
304
|
-
|
|
450
|
+
walkDiagnostic = "Pi package version was not found";
|
|
305
451
|
} catch (error) {
|
|
306
|
-
|
|
307
|
-
version: undefined,
|
|
308
|
-
diagnostic: `Pi package version detection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
309
|
-
};
|
|
452
|
+
walkDiagnostic = `Pi package version detection failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
310
453
|
}
|
|
454
|
+
const runtimeVersion = resolution.runtimeVersion !== undefined ? resolution.runtimeVersion : RUNTIME_PI_VERSION;
|
|
455
|
+
if (typeof runtimeVersion === "string" && runtimeVersion.trim() !== "") return { version: runtimeVersion };
|
|
456
|
+
return { version: undefined, diagnostic: walkDiagnostic };
|
|
311
457
|
}
|
|
312
458
|
|
|
313
459
|
function descriptorSnapshot(descriptor: PropertyDescriptor | undefined): CompatibilityDescriptorSnapshot | undefined {
|
|
@@ -108,6 +108,51 @@ export function resetAnsi(value: string): string {
|
|
|
108
108
|
export function fitAnsiWidth(value: string, width: number, ellipsis = "…"): string {
|
|
109
109
|
return visibleWidth(value) <= width ? value : truncateAnsi(value, width, ellipsis);
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Fit a string by dropping the HEAD (keeping the tail): for path-like values
|
|
113
|
+
* whose meaningful part is the end (the repo name). ANSI-aware — an escape run
|
|
114
|
+
* belongs to the visible character it precedes, so dropped characters drop
|
|
115
|
+
* their styling with them and the first kept character keeps its own escape.
|
|
116
|
+
*/
|
|
117
|
+
export function fitAnsiWidthTail(value: string, width: number, ellipsis = "…"): string {
|
|
118
|
+
if (width <= 0) return "";
|
|
119
|
+
if (visibleWidth(value) <= width) return resetAnsi(value);
|
|
120
|
+
const ellipsisWidth = visibleWidth(ellipsis);
|
|
121
|
+
if (width <= ellipsisWidth) return resetAnsi(ellipsis);
|
|
122
|
+
type Unit = { ansi: string; char: string; charWidth: number };
|
|
123
|
+
const units: Unit[] = [];
|
|
124
|
+
let pending = "";
|
|
125
|
+
for (let i = 0; i < value.length; i++) {
|
|
126
|
+
const code = value.charCodeAt(i);
|
|
127
|
+
if (code === 27) {
|
|
128
|
+
const start = i;
|
|
129
|
+
i++;
|
|
130
|
+
while (i + 1 < value.length && !isFinal(value[i + 1] ?? "")) i++;
|
|
131
|
+
i++;
|
|
132
|
+
pending += value.slice(start, i + 1);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
let char = value[i] ?? "";
|
|
136
|
+
// Keep surrogate pairs (emoji etc.) as one unit.
|
|
137
|
+
if (code >= 0xd800 && code <= 0xdbff && i + 1 < value.length) char += value[++i];
|
|
138
|
+
units.push({ ansi: pending, char, charWidth: visibleWidth(char) || 1 });
|
|
139
|
+
pending = "";
|
|
140
|
+
}
|
|
141
|
+
let kept = 0;
|
|
142
|
+
let used = ellipsisWidth;
|
|
143
|
+
while (kept < units.length) {
|
|
144
|
+
const unit = units[units.length - 1 - kept];
|
|
145
|
+
if (!unit || used + unit.charWidth > width) break;
|
|
146
|
+
used += unit.charWidth;
|
|
147
|
+
kept++;
|
|
148
|
+
}
|
|
149
|
+
let output = ellipsis;
|
|
150
|
+
for (let index = units.length - kept; index < units.length; index++) {
|
|
151
|
+
const unit = units[index];
|
|
152
|
+
if (unit) output += unit.ansi + unit.char;
|
|
153
|
+
}
|
|
154
|
+
return resetAnsi(output);
|
|
155
|
+
}
|
|
111
156
|
export function truncateAnsi(value: string, width: number, ellipsis = "…"): string {
|
|
112
157
|
if (width <= 0) return "";
|
|
113
158
|
if (visibleWidth(value) <= width) return resetAnsi(value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quandev104/pi-style",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "A native-layout, cohesive visual style package for Pi.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -42,18 +42,19 @@
|
|
|
42
42
|
"check": "npm run typecheck && npm run lint && npm run depcruise && npm run test && npm run build && npm run package:smoke"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@earendil-works/pi-agent-core": ">=0.83.0 <0.
|
|
46
|
-
"@earendil-works/pi-ai": ">=0.83.0 <0.
|
|
47
|
-
"@earendil-works/pi-coding-agent": ">=0.83.0 <0.
|
|
48
|
-
"@earendil-works/pi-tui": ">=0.83.0 <0.
|
|
45
|
+
"@earendil-works/pi-agent-core": ">=0.83.0 <0.86.0",
|
|
46
|
+
"@earendil-works/pi-ai": ">=0.83.0 <0.86.0",
|
|
47
|
+
"@earendil-works/pi-coding-agent": ">=0.83.0 <0.86.0",
|
|
48
|
+
"@earendil-works/pi-tui": ">=0.83.0 <0.86.0",
|
|
49
49
|
"typebox": ">=1.3.9 <2.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@biomejs/biome": "^2.5.0",
|
|
53
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
54
|
-
"@earendil-works/pi-ai": "^0.
|
|
55
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
56
|
-
"@earendil-works/pi-
|
|
53
|
+
"@earendil-works/pi-agent-core": "^0.85.0",
|
|
54
|
+
"@earendil-works/pi-ai": "^0.85.0",
|
|
55
|
+
"@earendil-works/pi-coding-agent": "^0.85.0",
|
|
56
|
+
"@earendil-works/pi-server": "^0.85.0",
|
|
57
|
+
"@earendil-works/pi-tui": "^0.85.0",
|
|
57
58
|
"@types/node": "^24.0.0",
|
|
58
59
|
"dependency-cruiser": "^17.4.3",
|
|
59
60
|
"git-cliff": "^2.13.1",
|