pi-cc-extensions 0.8.15 → 0.8.16
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.en.md +2 -2
- package/README.md +6 -65
- package/extensions/pi-aliases.ts +31 -0
- package/extensions/pi-startup-header.ts +288 -0
- package/package.json +3 -1
package/README.en.md
CHANGED
|
@@ -81,7 +81,6 @@ The `ask_user_question` tool is registered automatically.
|
|
|
81
81
|
| Structured questionnaires | Single choice, multiple choice, custom answers, Markdown previews, and RPC / ACP fallback | `ask_user_question` |
|
|
82
82
|
| Context inspection | Usage breakdown and previews for the system prompt, tools, skills, and messages | `/context` |
|
|
83
83
|
| Session references | Search and inject effective context from previous Sessions or existing SubAgents | `@session:` |
|
|
84
|
-
| Agent completion | Complete and delegate custom Agents from`~/.pi/agent/agents/*.md` | `@agent-name` |
|
|
85
84
|
| Theme | Includes the GitHub Dark Default theme | `/theme` |
|
|
86
85
|
|
|
87
86
|
### Output modes
|
|
@@ -165,10 +164,11 @@ Run `/reload` after changing extensions. Changes under `extensions/ask-user-ques
|
|
|
165
164
|
| `npm:pi-theme-picker` | Theme search and live preview |
|
|
166
165
|
| `npm:@ayulab/pi-rewind` | Checkpoint-based code or conversation rollback |
|
|
167
166
|
| `npm:pi-compact-thinking` | Compact rendering for hidden thinking blocks |
|
|
168
|
-
| `npm:pi-startup-header` | Theme-aware startup header |
|
|
169
167
|
|
|
170
168
|
## Credits
|
|
171
169
|
|
|
172
170
|
- Compact transcript behavior is based on [`avhagedorn/pi-compact-transcript`](https://github.com/avhagedorn/pi-compact-transcript) v0.6.2 (MIT).
|
|
173
171
|
- Structured questionnaires are based on [`@juicesharp/rpiv-ask-user-question`](https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-ask-user-question) v2.1.0 (MIT).
|
|
174
172
|
- Rich diffs are adapted from [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display) (MIT). See [`extensions/tool-diff/ATTRIBUTION.md`](./extensions/tool-diff/ATTRIBUTION.md).
|
|
173
|
+
- Startup header based on [`EnderLiquid/pi-startup-header`](https://github.com/EnderLiquid/pi-startup-header) (MIT).
|
|
174
|
+
- Command aliases based on [`xRyul/pi-aliases`](https://github.com/xRyul/pi-aliases) (MIT).
|
package/README.md
CHANGED
|
@@ -59,17 +59,12 @@
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
pi install npm:pi-cc-extensions
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
安装后执行 `/reload`,然后可以使用:
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/context # 查看上下文占用
|
|
69
|
-
@ # 补全 Agent、Session 与文件
|
|
63
|
+
# GitHub
|
|
64
|
+
pi install git:github.com/minuque/pi-cc-extensions
|
|
70
65
|
```
|
|
71
66
|
|
|
72
|
-
`
|
|
67
|
+
安装后执行 `/reload`
|
|
73
68
|
|
|
74
69
|
## 功能
|
|
75
70
|
|
|
@@ -81,62 +76,7 @@ pi install npm:pi-cc-extensions
|
|
|
81
76
|
| Ask User Question 结构化问卷 | 单选、多选、自定义回答、Markdown 预览及 RPC / ACP 降级 | `ask_user_question` |
|
|
82
77
|
| 上下文检查 | 查看上下文占用,并预览 System prompt、Tools、Skills 和消息内容 | `/context` |
|
|
83
78
|
| Session 引用 | 搜索并注入历史 Session 或现有 SubAgent 的有效上下文 | `@session:` |
|
|
84
|
-
|
|
|
85
|
-
| 主题 | 随包提供 GitHub Dark Default 主题 | `/theme` |
|
|
86
|
-
|
|
87
|
-
### 输出模式
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
| 模式 | 行为 |
|
|
91
|
-
| ----------- | ----------------------------------------------------------- |
|
|
92
|
-
| `on` | Claude Code 风格工具输出;`edit` / `write` 使用 rich diff |
|
|
93
|
-
| `off` | 使用 Pi 原生 renderer |
|
|
94
|
-
| `compact` | 单行工具预览、同名调用合并、运行耗时与总结 |
|
|
95
|
-
|
|
96
|
-
```text
|
|
97
|
-
/ccstyle on
|
|
98
|
-
/ccstyle off
|
|
99
|
-
/ccstyle compact
|
|
100
|
-
/ccstyle status
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
配置保存在 `~/.pi/agent/claude-code-style.json`:
|
|
104
|
-
|
|
105
|
-
```json
|
|
106
|
-
{
|
|
107
|
-
"mode": "on",
|
|
108
|
-
"excludeRenderers": [],
|
|
109
|
-
"fixedEditorFeatures": true
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
- `excludeRenderers` 使用精确工具名;`Agent` 始终保留专用 renderer。
|
|
114
|
-
- `fixedEditorFeatures: true` 启用 fixed editor 鼠标滚动、点击、viewport 映射和回到底部提示。
|
|
115
|
-
- 设为 `false` 会关闭终端鼠标上报,恢复终端原生滚轮;`Ctrl+End` 仍可用。
|
|
116
|
-
- 手动修改配置后执行 `/reload`。
|
|
117
|
-
|
|
118
|
-
### 结构化问卷
|
|
119
|
-
|
|
120
|
-
支持 1–4 个问题、每题 2–4 个选项、单选、多选、自定义回答和可选 Markdown 预览。TUI 会保留部分 transcript 空间;RPC / ACP 环境自动降级为原生对话框。
|
|
121
|
-
|
|
122
|
-
问卷配置位于 `~/.config/rpiv-ask-user-question/config.json`。快捷键与完整行为见 [`extensions/ask-user-question/README.md`](./extensions/ask-user-question/README.md)。
|
|
123
|
-
|
|
124
|
-
### `@` 引用
|
|
125
|
-
|
|
126
|
-
- 输入 `@session:` 引用历史 Session。
|
|
127
|
-
- 输入 `@agent-name` 委托自定义 Agent。
|
|
128
|
-
- 可与 `@bacnh85/pi-fff` 和 `@tintinweb/pi-subagents` 共存。
|
|
129
|
-
- Session 内容会去重并限制注入规模。
|
|
130
|
-
|
|
131
|
-
## 其他安装方式
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
# GitHub
|
|
135
|
-
pi install git:github.com/minuque/pi-cc-extensions
|
|
136
|
-
|
|
137
|
-
# 本地仓库
|
|
138
|
-
pi install /absolute/path/to/pi-cc-extensions
|
|
139
|
-
```
|
|
79
|
+
| 主题 | 随包提供内置 GitHub Dark Default 主题 | `/theme` |
|
|
140
80
|
|
|
141
81
|
## 本地开发
|
|
142
82
|
|
|
@@ -165,10 +105,11 @@ pi -e .
|
|
|
165
105
|
| `npm:pi-theme-picker` | 主题搜索和实时预览 |
|
|
166
106
|
| `npm:@ayulab/pi-rewind` | 基于 checkpoint 回退代码或对话 |
|
|
167
107
|
| `npm:pi-compact-thinking` | 紧凑显示隐藏的 thinking 块 |
|
|
168
|
-
| `npm:pi-startup-header` | 主题自适应启动标题 |
|
|
169
108
|
|
|
170
109
|
## 致谢
|
|
171
110
|
|
|
172
111
|
- Compact transcript 基于 [`avhagedorn/pi-compact-transcript`](https://github.com/avhagedorn/pi-compact-transcript) v0.6.2(MIT)。
|
|
173
112
|
- 结构化问卷基于 [`@juicesharp/rpiv-ask-user-question`](https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-ask-user-question) v2.1.0(MIT)。
|
|
174
113
|
- Rich diff 改编自 [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display)(MIT);详见 [`extensions/tool-diff/ATTRIBUTION.md`](./extensions/tool-diff/ATTRIBUTION.md)。
|
|
114
|
+
- 启动头基于 [`EnderLiquid/pi-startup-header`](https://github.com/EnderLiquid/pi-startup-header)(MIT)。
|
|
115
|
+
- 命令别名基于 [`xRyul/pi-aliases`](https://github.com/xRyul/pi-aliases)(MIT)。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Aliases Extension
|
|
3
|
+
*
|
|
4
|
+
* Registers command aliases that show in autocomplete:
|
|
5
|
+
* /clear → /new (start a new session)
|
|
6
|
+
* /exit → /quit (exit pi)
|
|
7
|
+
*
|
|
8
|
+
* Install:
|
|
9
|
+
* pi install npm:pi-aliases
|
|
10
|
+
* pi install git:github.com/xRyul/pi-aliases
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
|
|
15
|
+
export default function (pi: ExtensionAPI) {
|
|
16
|
+
// /clear → /new
|
|
17
|
+
pi.registerCommand("clear", {
|
|
18
|
+
description: "→ /new · Start a new session",
|
|
19
|
+
handler: async (_args, ctx) => {
|
|
20
|
+
await ctx.newSession();
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// /exit → /quit
|
|
25
|
+
pi.registerCommand("exit", {
|
|
26
|
+
description: "→ /quit · Exit pi",
|
|
27
|
+
handler: async (_args, ctx) => {
|
|
28
|
+
ctx.shutdown();
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
type Rgb = [number, number, number];
|
|
4
|
+
type StyledPart = {
|
|
5
|
+
raw: string;
|
|
6
|
+
styled: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const ANSI_RESET = "\x1b[0m";
|
|
10
|
+
const ANSI_PATTERN =
|
|
11
|
+
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
12
|
+
|
|
13
|
+
const LOGO_LINES = [
|
|
14
|
+
"████████████╗",
|
|
15
|
+
"████████████║",
|
|
16
|
+
"████╔═══████║",
|
|
17
|
+
"████║ ████║",
|
|
18
|
+
"████████╬═══████╗",
|
|
19
|
+
"████████║ ████║ ",
|
|
20
|
+
"████╔═══╝ ████║",
|
|
21
|
+
"████║ ████║",
|
|
22
|
+
"╚═══╝ ╚═══╝",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const TAGLINE_LINE_1 = "There are many agent harnesses,";
|
|
26
|
+
const TAGLINE_LINE_2_PREFIX = "but this one is ";
|
|
27
|
+
const TAGLINE_LINE_2_HIGHLIGHT = "yours";
|
|
28
|
+
const TAGLINE_LINE_2_SUFFIX = ".";
|
|
29
|
+
|
|
30
|
+
const FALLBACK_ACCENT_RGB: Rgb = [80, 160, 255];
|
|
31
|
+
const LOGO_BLOCK_WIDTH = Math.max(...LOGO_LINES.map((line) => [...line].length));
|
|
32
|
+
|
|
33
|
+
const PALETTE_STEPS = 24;
|
|
34
|
+
const PALETTE_MAX_DARKEN = 0.18;
|
|
35
|
+
const PALETTE_MAX_LIGHTEN = 0.18;
|
|
36
|
+
const LOGO_ROW_PHASE_STEP = 0.12;
|
|
37
|
+
|
|
38
|
+
const ANSI_16_RGB_TABLE: Rgb[] = [
|
|
39
|
+
[0, 0, 0],
|
|
40
|
+
[128, 0, 0],
|
|
41
|
+
[0, 128, 0],
|
|
42
|
+
[128, 128, 0],
|
|
43
|
+
[0, 0, 128],
|
|
44
|
+
[128, 0, 128],
|
|
45
|
+
[0, 128, 128],
|
|
46
|
+
[192, 192, 192],
|
|
47
|
+
[128, 128, 128],
|
|
48
|
+
[255, 0, 0],
|
|
49
|
+
[0, 255, 0],
|
|
50
|
+
[255, 255, 0],
|
|
51
|
+
[0, 0, 255],
|
|
52
|
+
[255, 0, 255],
|
|
53
|
+
[0, 255, 255],
|
|
54
|
+
[255, 255, 255],
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
function stripAnsi(text: string): string {
|
|
58
|
+
return text.replace(ANSI_PATTERN, "");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getVisibleLength(text: string): number {
|
|
62
|
+
return [...stripAnsi(text)].length;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function clampByte(value: number): number {
|
|
66
|
+
return Math.max(0, Math.min(255, Math.round(value)));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function interpolateChannel(start: number, end: number, factor: number): number {
|
|
70
|
+
return Math.round(start + (end - start) * factor);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function interpolateRgb(start: Rgb, end: Rgb, factor: number): Rgb {
|
|
74
|
+
return [
|
|
75
|
+
interpolateChannel(start[0], end[0], factor),
|
|
76
|
+
interpolateChannel(start[1], end[1], factor),
|
|
77
|
+
interpolateChannel(start[2], end[2], factor),
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function darkenRgb(rgb: Rgb, amount: number): Rgb {
|
|
82
|
+
return [
|
|
83
|
+
clampByte(rgb[0] * (1 - amount)),
|
|
84
|
+
clampByte(rgb[1] * (1 - amount)),
|
|
85
|
+
clampByte(rgb[2] * (1 - amount)),
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function lightenRgb(rgb: Rgb, amount: number): Rgb {
|
|
90
|
+
return [
|
|
91
|
+
clampByte(rgb[0] + (255 - rgb[0]) * amount),
|
|
92
|
+
clampByte(rgb[1] + (255 - rgb[1]) * amount),
|
|
93
|
+
clampByte(rgb[2] + (255 - rgb[2]) * amount),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function applyTruecolor(rgb: Rgb, text: string): string {
|
|
98
|
+
const [red, green, blue] = rgb;
|
|
99
|
+
return `\x1b[38;2;${red};${green};${blue}m${text}${ANSI_RESET}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function ansi16ToRgb(index: number): Rgb {
|
|
103
|
+
return ANSI_16_RGB_TABLE[index] ?? [255, 255, 255];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function ansi256ToRgb(index: number): Rgb {
|
|
107
|
+
if (index < 16) return ansi16ToRgb(index);
|
|
108
|
+
|
|
109
|
+
if (index >= 232) {
|
|
110
|
+
const gray = 8 + (index - 232) * 10;
|
|
111
|
+
return [gray, gray, gray];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const cubeIndex = index - 16;
|
|
115
|
+
const redIndex = Math.floor(cubeIndex / 36);
|
|
116
|
+
const greenIndex = Math.floor((cubeIndex % 36) / 6);
|
|
117
|
+
const blueIndex = cubeIndex % 6;
|
|
118
|
+
const values = [0, 95, 135, 175, 215, 255];
|
|
119
|
+
|
|
120
|
+
return [values[redIndex]!, values[greenIndex]!, values[blueIndex]!];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parseTruecolorAnsi(ansi: string): Rgb | undefined {
|
|
124
|
+
const match = ansi.match(/38;2;(\d+);(\d+);(\d+)/);
|
|
125
|
+
if (!match) return undefined;
|
|
126
|
+
|
|
127
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseAnsi256Foreground(ansi: string): Rgb | undefined {
|
|
131
|
+
const match = ansi.match(/38;5;(\d+)/);
|
|
132
|
+
if (!match) return undefined;
|
|
133
|
+
|
|
134
|
+
return ansi256ToRgb(Number(match[1]));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseAnsi16Foreground(ansi: string): Rgb | undefined {
|
|
138
|
+
const normalMatch = ansi.match(/(?:\[|;)(3[0-7])(?:;|m)/);
|
|
139
|
+
if (normalMatch) {
|
|
140
|
+
return ansi16ToRgb(Number(normalMatch[1]) - 30);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const brightMatch = ansi.match(/(?:\[|;)(9[0-7])(?:;|m)/);
|
|
144
|
+
if (brightMatch) {
|
|
145
|
+
return ansi16ToRgb(Number(brightMatch[1]) - 90 + 8);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function parseForegroundRgbFromAnsi(ansi: string): Rgb | undefined {
|
|
152
|
+
return parseTruecolorAnsi(ansi) ?? parseAnsi256Foreground(ansi) ?? parseAnsi16Foreground(ansi);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function resolveAccentRgb(theme: { getFgAnsi(name: string): string }): Rgb {
|
|
156
|
+
return parseForegroundRgbFromAnsi(theme.getFgAnsi("accent")) ?? FALLBACK_ACCENT_RGB;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function buildAccentPalette(accent: Rgb): Rgb[] {
|
|
160
|
+
return Array.from({ length: PALETTE_STEPS }, (_, index) => {
|
|
161
|
+
const progress = index / PALETTE_STEPS;
|
|
162
|
+
const wave = -Math.cos(progress * Math.PI * 2);
|
|
163
|
+
|
|
164
|
+
if (wave < 0) {
|
|
165
|
+
return darkenRgb(accent, PALETTE_MAX_DARKEN * -wave);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return lightenRgb(accent, PALETTE_MAX_LIGHTEN * wave);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function sampleGradientColor(palette: Rgb[], position: number): Rgb {
|
|
173
|
+
const wrappedPosition = ((position % 1) + 1) % 1;
|
|
174
|
+
const scaledPosition = wrappedPosition * palette.length;
|
|
175
|
+
const baseIndex = Math.floor(scaledPosition) % palette.length;
|
|
176
|
+
const nextIndex = (baseIndex + 1) % palette.length;
|
|
177
|
+
const factor = scaledPosition - Math.floor(scaledPosition);
|
|
178
|
+
|
|
179
|
+
return interpolateRgb(palette[baseIndex]!, palette[nextIndex]!, factor);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function renderGradientText(text: string, palette: Rgb[], phase: number): string {
|
|
183
|
+
const characters = [...text];
|
|
184
|
+
const span = Math.max(characters.length - 1, 1);
|
|
185
|
+
|
|
186
|
+
return characters
|
|
187
|
+
.map((character, index) => {
|
|
188
|
+
if (character === " ") return character;
|
|
189
|
+
const color = sampleGradientColor(palette, index / span + phase);
|
|
190
|
+
return applyTruecolor(color, character);
|
|
191
|
+
})
|
|
192
|
+
.join("");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function createCenteredBlockLine(text: string, width: number, blockWidth: number): string {
|
|
196
|
+
const leftPadding = Math.max(0, Math.floor((width - blockWidth) / 2));
|
|
197
|
+
return `${" ".repeat(leftPadding)}${text}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function createCenteredStyledLine(parts: StyledPart[], width: number): string {
|
|
201
|
+
const rawText = parts.map((part) => part.raw).join("");
|
|
202
|
+
const leftPadding = Math.max(0, Math.floor((width - [...rawText].length) / 2));
|
|
203
|
+
const styledText = parts.map((part) => part.styled).join("");
|
|
204
|
+
return `${" ".repeat(leftPadding)}${styledText}`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function fitLineToWidth(line: string, width: number): string {
|
|
208
|
+
if (getVisibleLength(line) <= width) {
|
|
209
|
+
return line;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return stripAnsi(line).slice(0, width);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function renderLogoLines(width: number, palette: Rgb[]): string[] {
|
|
216
|
+
return LOGO_LINES.map((line, rowIndex) => {
|
|
217
|
+
const phasedLine = renderGradientText(line, palette, rowIndex * LOGO_ROW_PHASE_STEP);
|
|
218
|
+
return createCenteredBlockLine(phasedLine, width, LOGO_BLOCK_WIDTH);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function renderTaglineLines(
|
|
223
|
+
width: number,
|
|
224
|
+
theme: {
|
|
225
|
+
fg(name: string, text: string): string;
|
|
226
|
+
bold(text: string): string;
|
|
227
|
+
},
|
|
228
|
+
): string[] {
|
|
229
|
+
const line1 = createCenteredStyledLine(
|
|
230
|
+
[{ raw: TAGLINE_LINE_1, styled: theme.fg("accent", TAGLINE_LINE_1) }],
|
|
231
|
+
width,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const line2 = createCenteredStyledLine(
|
|
235
|
+
[
|
|
236
|
+
{
|
|
237
|
+
raw: TAGLINE_LINE_2_PREFIX,
|
|
238
|
+
styled: theme.fg("accent", TAGLINE_LINE_2_PREFIX),
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
raw: TAGLINE_LINE_2_HIGHLIGHT,
|
|
242
|
+
styled: theme.bold(theme.fg("mdLink", TAGLINE_LINE_2_HIGHLIGHT)),
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
raw: TAGLINE_LINE_2_SUFFIX,
|
|
246
|
+
styled: theme.fg("accent", TAGLINE_LINE_2_SUFFIX),
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
width,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return [line1, line2];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function renderHeaderLines(
|
|
256
|
+
width: number,
|
|
257
|
+
theme: {
|
|
258
|
+
getFgAnsi(name: string): string;
|
|
259
|
+
fg(name: string, text: string): string;
|
|
260
|
+
bold(text: string): string;
|
|
261
|
+
},
|
|
262
|
+
): string[] {
|
|
263
|
+
const accentRgb = resolveAccentRgb(theme);
|
|
264
|
+
const palette = buildAccentPalette(accentRgb);
|
|
265
|
+
const logoLines = renderLogoLines(width, palette);
|
|
266
|
+
const taglineLines = renderTaglineLines(width, theme);
|
|
267
|
+
|
|
268
|
+
return ["", ...logoLines, "", ...taglineLines, ""].map((line) => fitLineToWidth(line, width));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export default function piStartupHeader(pi: ExtensionAPI) {
|
|
272
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
273
|
+
if (!ctx.hasUI) return;
|
|
274
|
+
|
|
275
|
+
ctx.ui.setHeader((_tui, theme) => ({
|
|
276
|
+
render(width: number): string[] {
|
|
277
|
+
return renderHeaderLines(width, theme);
|
|
278
|
+
},
|
|
279
|
+
invalidate() {},
|
|
280
|
+
}));
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
284
|
+
if (!ctx.hasUI) return;
|
|
285
|
+
|
|
286
|
+
ctx.ui.setHeader(undefined);
|
|
287
|
+
});
|
|
288
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cc-extensions",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.16",
|
|
4
4
|
"description": "A Pi productivity suite with Claude Code-style UI, structured questionnaires, context inspection, and agent/session references",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
],
|
|
28
28
|
"pi": {
|
|
29
29
|
"extensions": [
|
|
30
|
+
"./extensions/pi-aliases.ts",
|
|
31
|
+
"./extensions/pi-startup-header.ts",
|
|
30
32
|
"./extensions/claude-code-style.ts",
|
|
31
33
|
"./extensions/context.ts",
|
|
32
34
|
"./extensions/session-reference.ts",
|