@quandev104/pi-style 0.1.4 → 0.1.6
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 +30 -0
- package/README.md +10 -6
- package/dist/extensions/pi-style.js +3939 -1431
- package/dist/extensions/pi-style.js.map +1 -1
- package/extension-src/pi-style/app/command-service.ts +2 -0
- package/extension-src/pi-style/domain/config-authorization.ts +6 -3
- package/extension-src/pi-style/domain/config-normalization.ts +21 -5
- package/extension-src/pi-style/domain/config-presets.ts +1 -1
- package/extension-src/pi-style/domain/config-types.ts +7 -3
- package/extension-src/pi-style/domain/theme.ts +6 -1
- package/extension-src/pi-style/features/editor/index.ts +169 -27
- package/extension-src/pi-style/features/messages/index.ts +66 -0
- package/extension-src/pi-style/features/tools/bash-execution.ts +112 -0
- package/extension-src/pi-style/features/tools/boxed/bash.ts +154 -130
- package/extension-src/pi-style/features/tools/boxed/batch.ts +50 -27
- package/extension-src/pi-style/features/tools/boxed/command-shape.ts +136 -0
- package/extension-src/pi-style/features/tools/boxed/find.ts +2 -2
- package/extension-src/pi-style/features/tools/boxed/gh.ts +1012 -0
- package/extension-src/pi-style/features/tools/boxed/git.ts +1960 -0
- package/extension-src/pi-style/features/tools/boxed/grep.ts +2 -2
- package/extension-src/pi-style/features/tools/boxed/output-tree.ts +9 -10
- package/extension-src/pi-style/features/tools/boxed/read.ts +3 -3
- package/extension-src/pi-style/features/tools/boxed/write.ts +2 -1
- package/extension-src/pi-style/pi/compatibility-coordinator.ts +32 -11
- package/extension-src/pi-style/pi/compatibility-probe.ts +341 -205
- package/extension-src/pi-style/pi/compatibility-registry.ts +19 -3
- package/extension-src/pi-style/pi/index.ts +21 -3
- package/extension-src/pi-style/pi/session-coordinator.ts +24 -0
- package/extension-src/pi-style/shared/box.ts +8 -4
- package/extension-src/pi-style/shared/split-diff.ts +9 -9
- package/package.json +9 -9
- package/themes/titanium-light.json +82 -0
- package/themes/titanium.json +79 -0
- package/themes/.gitkeep +0 -0
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import type { Component } from "@earendil-works/pi-tui";
|
|
15
15
|
import { stripAnsi } from "../../../shared/ansi.js";
|
|
16
|
-
import { type BoxTheme, getTextOutput, shortenPath } from "../../../shared/box.js";
|
|
16
|
+
import { type BoxTheme, dimLine, getTextOutput, shortenPath } from "../../../shared/box.js";
|
|
17
17
|
import { safeTruncateToWidth } from "../../../shared/render-budget.js";
|
|
18
18
|
import {
|
|
19
19
|
type GrepMatch,
|
|
@@ -111,7 +111,7 @@ function renderErrorLines(theme: BoxTheme, errorText: string, width: number): st
|
|
|
111
111
|
.map((line) => line.trim())
|
|
112
112
|
.filter((line) => line.length > 0);
|
|
113
113
|
if (raw.length === 0) return [];
|
|
114
|
-
const prefix = `${TREE_INDENT}${
|
|
114
|
+
const prefix = `${TREE_INDENT}${dimLine("└─")} `;
|
|
115
115
|
const out = raw
|
|
116
116
|
.slice(0, GREP_ERROR_LINES)
|
|
117
117
|
.map((line) => safeTruncateToWidth(`${prefix}${theme.fg("error", line)}`, Math.max(1, width), "…"));
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
// as one visual family.
|
|
21
21
|
|
|
22
22
|
import type { BoxTheme } from "../../../shared/box.js";
|
|
23
|
+
import { dimLine } from "../../../shared/box.js";
|
|
23
24
|
import { safeTruncateToWidth } from "../../../shared/render-budget.js";
|
|
24
25
|
|
|
25
26
|
/** Indent for top-level tree rows; matches the quiet-tool batch panel. */
|
|
@@ -251,11 +252,11 @@ export function renderOutputTree(
|
|
|
251
252
|
const lastIndex = visible.length - 1;
|
|
252
253
|
for (let i = 0; i < visible.length; i++) {
|
|
253
254
|
const branch = i < lastIndex || more > 0 ? "├─" : "└─";
|
|
254
|
-
const line = `${indent}${
|
|
255
|
+
const line = `${indent}${dimLine(branch)} ${theme.fg(entryColor, label(visible[i] ?? ""))}`;
|
|
255
256
|
out.push(safeTruncateToWidth(line, safeWidth, "…"));
|
|
256
257
|
}
|
|
257
258
|
if (more > 0) {
|
|
258
|
-
const line = `${indent}${
|
|
259
|
+
const line = `${indent}${dimLine("└─")} ${theme.fg("dim", `… ${more} more ${pluralForm(moreUnit, more)}`)}`;
|
|
259
260
|
out.push(safeTruncateToWidth(line, safeWidth, "…"));
|
|
260
261
|
}
|
|
261
262
|
return out;
|
|
@@ -274,7 +275,7 @@ function formatMatchRow(theme: BoxTheme, match: GrepMatch): string {
|
|
|
274
275
|
// Match rows render in the output text color (not primary) so they read like
|
|
275
276
|
// the matched code; only the file nodes carry the primary color.
|
|
276
277
|
const label = theme.fg("toolOutput", `*${match.line}`);
|
|
277
|
-
const sep =
|
|
278
|
+
const sep = dimLine("│");
|
|
278
279
|
return `${label}${sep} ${theme.fg("toolOutput", match.content)}`;
|
|
279
280
|
}
|
|
280
281
|
|
|
@@ -313,7 +314,7 @@ export function renderGrepTree(
|
|
|
313
314
|
if (singleFile) {
|
|
314
315
|
budget.forEach((match, index) => {
|
|
315
316
|
const isLast = index === totalVisible - 1 && !truncated;
|
|
316
|
-
push(`${indent}${
|
|
317
|
+
push(`${indent}${dimLine(isLast ? "└─" : "├─")} ${formatMatchRow(theme, match)}`);
|
|
317
318
|
});
|
|
318
319
|
} else {
|
|
319
320
|
// Walk the budget, tracking position within each file group so the file
|
|
@@ -323,7 +324,7 @@ export function renderGrepTree(
|
|
|
323
324
|
const group = groups[gi];
|
|
324
325
|
if (!group) continue;
|
|
325
326
|
const isLastGroup = gi === groups.length - 1;
|
|
326
|
-
const trunk = isLastGroup ? " " :
|
|
327
|
+
const trunk = isLastGroup ? " " : dimLine("│");
|
|
327
328
|
|
|
328
329
|
const visibleHere: GrepMatch[] = [];
|
|
329
330
|
for (const match of group.matches) {
|
|
@@ -336,22 +337,20 @@ export function renderGrepTree(
|
|
|
336
337
|
const groupIsLastRendered = shown >= totalVisible && !truncated;
|
|
337
338
|
const fileLabel = options.withIcons ? `${fileIcon(group.file)} ${group.file}` : group.file;
|
|
338
339
|
// File nodes use the primary (accent) color, matching read/ls/find paths.
|
|
339
|
-
push(`${indent}${
|
|
340
|
+
push(`${indent}${dimLine(groupIsLastRendered ? "└─" : "├─")} ${theme.fg("accent", fileLabel)}`);
|
|
340
341
|
|
|
341
342
|
visibleHere.forEach((match, index) => {
|
|
342
343
|
const isLastInGroup = index === visibleHere.length - 1;
|
|
343
344
|
const isLastOverall = groupIsLastRendered && isLastInGroup;
|
|
344
345
|
push(
|
|
345
|
-
`${indent}${trunk}${TREE_CHILD_INDENT}${
|
|
346
|
+
`${indent}${trunk}${TREE_CHILD_INDENT}${dimLine(isLastOverall ? "└─" : "├─")} ${formatMatchRow(theme, match)}`,
|
|
346
347
|
);
|
|
347
348
|
});
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
if (truncated) {
|
|
352
|
-
push(
|
|
353
|
-
`${indent}${theme.fg("borderMuted", "└─")} ${theme.fg("dim", `… ${remaining} more ${pluralForm("match", remaining)}`)}`,
|
|
354
|
-
);
|
|
353
|
+
push(`${indent}${dimLine("└─")} ${theme.fg("dim", `… ${remaining} more ${pluralForm("match", remaining)}`)}`);
|
|
355
354
|
}
|
|
356
355
|
return out;
|
|
357
356
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Boxed read tool renderer
|
|
2
2
|
// (renderCall/renderResult only; no tool re-registration).
|
|
3
3
|
//
|
|
4
|
-
// Read calls render
|
|
5
|
-
// consecutive reads group into one panel (see
|
|
6
|
-
//
|
|
4
|
+
// Read calls render boxless: a lone read is a single inline line
|
|
5
|
+
// (`➔ Read <path>`), consecutive reads group into one tree panel (see
|
|
6
|
+
// batch.ts).
|
|
7
7
|
|
|
8
8
|
import { stripAnsi } from "../../../shared/ansi.js";
|
|
9
9
|
import { getTextOutput } from "../../../shared/box.js";
|
|
@@ -15,6 +15,7 @@ import { stripAnsi } from "../../../shared/ansi.js";
|
|
|
15
15
|
import {
|
|
16
16
|
type BoxTheme,
|
|
17
17
|
boxedToolWidthKey,
|
|
18
|
+
dimLine,
|
|
18
19
|
getTextOutput,
|
|
19
20
|
renderBoxedToolResult,
|
|
20
21
|
renderCompactBoxedToolCall,
|
|
@@ -62,7 +63,7 @@ function numberedPreviewLines(content: string): NumberedLine[] {
|
|
|
62
63
|
|
|
63
64
|
/** One boxed preview row: dim gutter + toolOutput content. */
|
|
64
65
|
function formatNumberedLine(theme: BoxTheme, line: NumberedLine): string {
|
|
65
|
-
return `${
|
|
66
|
+
return `${dimLine(`${line.number} `)}${theme.fg("toolOutput", line.content)}`;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
/** Compact write box: path header, numbered content preview, metrics footer. */
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
detectPiVersion,
|
|
7
7
|
disposePiCompatibilityProbe,
|
|
8
8
|
probePiCompatibility,
|
|
9
|
+
SUPPORTED_VERSION_RANGE,
|
|
9
10
|
} from "./compatibility-probe.js";
|
|
10
11
|
|
|
11
12
|
export interface CompatibilityCoordinator {
|
|
@@ -60,8 +61,10 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
60
61
|
report?.unsupported.filter((item) =>
|
|
61
62
|
feature === "messages" ? item.subtype.includes("message") : item.subtype.includes("tool"),
|
|
62
63
|
) ?? [];
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
// Identity drift degrades only the affected surface to native (graceful);
|
|
65
|
+
// a feature is only "failed" when an install/shape error occurred.
|
|
66
|
+
const failed = records.some((item) => /failed|rejected|rolled back|shape is not/i.test(item.reason));
|
|
67
|
+
const fallback = records.some((item) => /fallback|authorization|disabled|identity/i.test(item.reason));
|
|
65
68
|
const authorized = Boolean(authorization?.core && surfaceAuthorized);
|
|
66
69
|
const installedRecord = report?.recordSnapshots.some(
|
|
67
70
|
(item) => item.feature === feature && !item.disposed && (subtype === undefined || item.subtype === subtype),
|
|
@@ -69,7 +72,7 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
69
72
|
return {
|
|
70
73
|
configured,
|
|
71
74
|
authorized,
|
|
72
|
-
installed: Boolean(installedRecord &&
|
|
75
|
+
installed: Boolean(installedRecord && authorized && configured),
|
|
73
76
|
conflicted: records.some((item) => item.reason.includes("owner")),
|
|
74
77
|
failed,
|
|
75
78
|
cleanupPending,
|
|
@@ -81,12 +84,13 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
81
84
|
configured: messagesConfigured || toolsConfigured,
|
|
82
85
|
authorized: authorization?.core ?? false,
|
|
83
86
|
installed: report !== undefined,
|
|
84
|
-
conflicted: report?.unsupported.some((item) => item.reason.includes("
|
|
85
|
-
failed: report?.unsupported.some((item) => item.reason
|
|
87
|
+
conflicted: report?.unsupported.some((item) => item.reason.includes("owner")) ?? false,
|
|
88
|
+
failed: report?.unsupported.some((item) => /failed|rejected|rolled back/i.test(item.reason)) ?? false,
|
|
86
89
|
cleanupPending,
|
|
87
|
-
nativeFallbacks: report?.unsupported.filter((item) => item.reason
|
|
90
|
+
nativeFallbacks: report?.unsupported.filter((item) => /fallback|identity/i.test(item.reason)).length ?? 0,
|
|
88
91
|
piVersion: version.version ?? report?.piVersion ?? "unknown",
|
|
89
|
-
versionRange: report?.versionRange ??
|
|
92
|
+
versionRange: report?.versionRange ?? SUPPORTED_VERSION_RANGE,
|
|
93
|
+
supportedVersions: report?.supportedVersions ?? [],
|
|
90
94
|
assistantMessage: surface(
|
|
91
95
|
"messages",
|
|
92
96
|
config.enabled && config.messages.enabled && config.messages.assistantPrefix,
|
|
@@ -105,11 +109,13 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
105
109
|
const productDenied = productGate === "deny";
|
|
106
110
|
if (cleanupPending && !report) cleanupPending = false;
|
|
107
111
|
if (cleanupPending || !tui || !config.enabled || !authorization?.core || productDenied) return undefined;
|
|
108
|
-
|
|
112
|
+
// Certification is per-surface identity (fingerprint) based, never pinned to
|
|
113
|
+
// a Pi version: authorization only needs the session flags + config. Version
|
|
114
|
+
// drift that preserves identities keeps working; changed identities degrade
|
|
115
|
+
// per-surface inside the probe.
|
|
109
116
|
const assistantEnabled =
|
|
110
117
|
authorization.assistant &&
|
|
111
118
|
isTierCAuthorized({
|
|
112
|
-
certifiedHost,
|
|
113
119
|
coreFlag: authorization.core,
|
|
114
120
|
surfaceFlag: true,
|
|
115
121
|
surface: "assistantMessage",
|
|
@@ -118,16 +124,29 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
118
124
|
const specialBlocksEnabled =
|
|
119
125
|
authorization.specialBlocks &&
|
|
120
126
|
isTierCAuthorized({
|
|
121
|
-
certifiedHost,
|
|
122
127
|
coreFlag: authorization.core,
|
|
123
128
|
surfaceFlag: true,
|
|
124
129
|
surface: "specialBlocks",
|
|
125
130
|
config,
|
|
126
131
|
});
|
|
127
132
|
const messagesEnabled = (assistantEnabled || specialBlocksEnabled) && config.messages.enabled;
|
|
133
|
+
// The hidden-thinking collapse is an assistant-message surface patch: it needs
|
|
134
|
+
// the assistant flag and `messages.hideThinkingLabel`, independent of the
|
|
135
|
+
// assistant prefix feature.
|
|
136
|
+
const thinkingCollapseEnabled = Boolean(
|
|
137
|
+
authorization.assistant &&
|
|
138
|
+
config.messages.enabled &&
|
|
139
|
+
config.messages.hideThinkingLabel &&
|
|
140
|
+
isTierCAuthorized({
|
|
141
|
+
coreFlag: authorization.core,
|
|
142
|
+
surfaceFlag: true,
|
|
143
|
+
surface: "messages",
|
|
144
|
+
config,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
128
147
|
const toolsEnabled =
|
|
129
148
|
authorization.tools &&
|
|
130
|
-
isTierCAuthorized({
|
|
149
|
+
isTierCAuthorized({ coreFlag: authorization.core, surfaceFlag: true, surface: "tools", config });
|
|
131
150
|
if (!messagesEnabled && !toolsEnabled) return undefined;
|
|
132
151
|
const detected = detectPiVersion();
|
|
133
152
|
report = probePiCompatibility(detected.version, {
|
|
@@ -137,6 +156,7 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
137
156
|
...config.messages,
|
|
138
157
|
enabled: messagesEnabled,
|
|
139
158
|
assistantPrefix: assistantEnabled,
|
|
159
|
+
hideThinkingLabel: thinkingCollapseEnabled,
|
|
140
160
|
specialBlocks: messagesEnabled && config.messages.specialBlocks && specialBlocksEnabled,
|
|
141
161
|
},
|
|
142
162
|
tools: {
|
|
@@ -147,6 +167,7 @@ export function createCompatibilityCoordinator(dispose = disposePiCompatibilityP
|
|
|
147
167
|
messageSnapshot: {
|
|
148
168
|
assistantPrefix: authorization.ascii ? "[assistant] " : "│ ",
|
|
149
169
|
assistantEnabled,
|
|
170
|
+
collapseHiddenThinking: thinkingCollapseEnabled,
|
|
150
171
|
},
|
|
151
172
|
toolSnapshot: {
|
|
152
173
|
callMarker: authorization.ascii ? "[tool] " : "[tool] ",
|