@herbertgao/pi-extensions 2026.8.8 → 2026.8.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/README.md +3 -3
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +24 -0
- package/node_modules/@czottmann/pi-automode/README.md +78 -112
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +18 -14
- package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +44 -0
- package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +5 -0
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +198 -104
- package/node_modules/@czottmann/pi-automode/docs/configuration.md +155 -0
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +55 -14
- package/node_modules/@czottmann/pi-automode/docs/diagnostics.md +90 -0
- package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +61 -26
- package/node_modules/@czottmann/pi-automode/examples/automode.local.json +5 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +172 -18
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +127 -30
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +3 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +273 -31
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/log.ts +60 -5
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +92 -8
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +203 -13
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/state.ts +1 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +11 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/utils.ts +9 -1
- package/node_modules/@czottmann/pi-automode/package.json +9 -2
- package/node_modules/@czottmann/pi-automode/skills/automode-diagnostics/SKILL.md +63 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +8 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +56 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +15 -13
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +29 -0
- package/node_modules/@herbertgao/pi-handoff/README.md +15 -18
- package/node_modules/@herbertgao/pi-handoff/package.json +11 -4
- package/node_modules/@herbertgao/pi-handoff/src/complete-text.ts +33 -0
- package/node_modules/@herbertgao/pi-handoff/src/index.ts +127 -232
- package/node_modules/@herbertgao/pi-handoff/src/session-query.ts +15 -30
- package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +14 -0
- package/node_modules/@herbertgao/pi-mermaid-open/README.md +21 -4
- package/node_modules/@herbertgao/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +11 -0
- package/node_modules/@herbertgao/pi-mermaid-open/herdr-plugin/viewer.mjs +305 -0
- package/node_modules/@herbertgao/pi-mermaid-open/package.json +15 -4
- package/node_modules/@herbertgao/pi-mermaid-open/src/index.ts +351 -101
- package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-preferred-thinking/README.md +30 -14
- package/node_modules/@herbertgao/pi-preferred-thinking/package.json +7 -4
- package/node_modules/@herbertgao/pi-preferred-thinking/src/index.ts +140 -85
- package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-recap/README.md +5 -3
- package/node_modules/@herbertgao/pi-recap/package.json +9 -3
- package/node_modules/@herbertgao/pi-recap/src/index.ts +3 -7
- package/node_modules/@herbertgao/pi-recap/src/model-picker.ts +6 -19
- package/node_modules/@herbertgao/pi-recap/src/models.ts +9 -21
- package/node_modules/@herbertgao/pi-recap/src/tui.ts +1 -1
- package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +49 -0
- package/node_modules/@herbertgao/pi-rename/README.md +13 -11
- package/node_modules/@herbertgao/pi-rename/package.json +13 -3
- package/node_modules/@herbertgao/pi-rename/src/index.ts +135 -59
- package/node_modules/@herbertgao/pi-rename/src/models.ts +5 -13
- package/node_modules/@herbertgao/pi-rename/src/naming.ts +2 -6
- package/node_modules/@herbertgao/pi-stash/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +16 -3
- package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +15 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +16 -0
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +5 -1
- package/node_modules/pi-web-access/CHANGELOG.md +13 -0
- package/node_modules/pi-web-access/README.md +2 -2
- package/node_modules/pi-web-access/chrome-cookies.ts +12 -7
- package/node_modules/pi-web-access/gemini-search.ts +28 -10
- package/node_modules/pi-web-access/index.ts +24 -10
- package/node_modules/pi-web-access/package.json +1 -1
- package/package.json +13 -12
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import net from "node:net"
|
|
2
|
+
import { readFile, rm } from "node:fs/promises"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {
|
|
5
|
+
allocateImageId,
|
|
6
|
+
deleteKittyImage,
|
|
7
|
+
encodeKitty,
|
|
8
|
+
getPngDimensions,
|
|
9
|
+
} from "@earendil-works/pi-tui"
|
|
10
|
+
|
|
11
|
+
const pngPath = process.env.PI_MERMAID_OPEN_PNG
|
|
12
|
+
const temporaryDirectory = pngPath ? path.dirname(pngPath) : undefined
|
|
13
|
+
const herdrSocketPath = process.env.HERDR_SOCKET_PATH
|
|
14
|
+
const herdrPaneId = process.env.HERDR_PANE_ID
|
|
15
|
+
const zoomSteps = [1, 1.25, 1.5, 2, 3]
|
|
16
|
+
const panStep = 5
|
|
17
|
+
|
|
18
|
+
let closing = false
|
|
19
|
+
|
|
20
|
+
function calculateImageCellSize(image, maxWidth, maxHeight, cell) {
|
|
21
|
+
const scale = Math.min(
|
|
22
|
+
(maxWidth * cell.widthPx) / image.widthPx,
|
|
23
|
+
(maxHeight * cell.heightPx) / image.heightPx,
|
|
24
|
+
)
|
|
25
|
+
return {
|
|
26
|
+
columns: Math.ceil((image.widthPx * scale) / cell.widthPx),
|
|
27
|
+
rows: Math.ceil((image.heightPx * scale) / cell.heightPx),
|
|
28
|
+
scale,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function encodeKittyFrame(
|
|
33
|
+
base64Data,
|
|
34
|
+
imageId,
|
|
35
|
+
columns,
|
|
36
|
+
rows,
|
|
37
|
+
sourceX,
|
|
38
|
+
sourceY,
|
|
39
|
+
sourceWidth,
|
|
40
|
+
sourceHeight,
|
|
41
|
+
) {
|
|
42
|
+
const sequence = encodeKitty(base64Data, {
|
|
43
|
+
columns,
|
|
44
|
+
rows,
|
|
45
|
+
imageId,
|
|
46
|
+
moveCursor: false,
|
|
47
|
+
})
|
|
48
|
+
return sequence.replace(
|
|
49
|
+
"\x1b_G",
|
|
50
|
+
`\x1b_Gx=${sourceX},y=${sourceY},w=${sourceWidth},h=${sourceHeight},`,
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function herdrRequest(method, params) {
|
|
55
|
+
if (!herdrSocketPath || !herdrPaneId) {
|
|
56
|
+
return Promise.reject(new Error("Herdr graphics context is missing"))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const socket = net.createConnection(herdrSocketPath)
|
|
61
|
+
let buffer = ""
|
|
62
|
+
let settled = false
|
|
63
|
+
const timeout = setTimeout(
|
|
64
|
+
() => settle(reject, new Error(`Herdr request timed out: ${method}`)),
|
|
65
|
+
1500,
|
|
66
|
+
)
|
|
67
|
+
const settle = (callback, value) => {
|
|
68
|
+
if (settled) return
|
|
69
|
+
settled = true
|
|
70
|
+
clearTimeout(timeout)
|
|
71
|
+
socket.destroy()
|
|
72
|
+
callback(value)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
socket.setEncoding("utf8")
|
|
76
|
+
socket.once("error", (error) => settle(reject, error))
|
|
77
|
+
socket.on("data", (chunk) => {
|
|
78
|
+
buffer += chunk
|
|
79
|
+
const newline = buffer.indexOf("\n")
|
|
80
|
+
if (newline === -1) return
|
|
81
|
+
|
|
82
|
+
let response
|
|
83
|
+
try {
|
|
84
|
+
response = JSON.parse(buffer.slice(0, newline))
|
|
85
|
+
} catch {
|
|
86
|
+
settle(reject, new Error("Invalid Herdr API response"))
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
if (response.error) {
|
|
90
|
+
settle(
|
|
91
|
+
reject,
|
|
92
|
+
new Error(
|
|
93
|
+
response.error.message ?? `Herdr request failed: ${method}`,
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
} else {
|
|
97
|
+
settle(resolve, response.result)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
socket.once("connect", () => {
|
|
101
|
+
socket.write(
|
|
102
|
+
JSON.stringify({ id: "pi-mermaid-open", method, params }) + "\n",
|
|
103
|
+
)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function finish(clearGraphics = async () => {}) {
|
|
109
|
+
if (closing) return
|
|
110
|
+
closing = true
|
|
111
|
+
|
|
112
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false)
|
|
113
|
+
process.stdin.pause()
|
|
114
|
+
try {
|
|
115
|
+
await clearGraphics()
|
|
116
|
+
} catch {
|
|
117
|
+
// The Herdr pane may already be gone.
|
|
118
|
+
}
|
|
119
|
+
process.stdout.write("\x1b[2J\x1b[H")
|
|
120
|
+
|
|
121
|
+
if (temporaryDirectory) {
|
|
122
|
+
await rm(temporaryDirectory, { recursive: true, force: true })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
process.exit(0)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function waitForClose(onKey, clearGraphics) {
|
|
129
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true)
|
|
130
|
+
process.stdin.resume()
|
|
131
|
+
process.stdin.on("data", (data) => {
|
|
132
|
+
const input = data.toString("utf8")
|
|
133
|
+
if (
|
|
134
|
+
input === "\x03" ||
|
|
135
|
+
input === "\x1b" ||
|
|
136
|
+
input === "\r" ||
|
|
137
|
+
input === "\n"
|
|
138
|
+
) {
|
|
139
|
+
void finish(clearGraphics)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const key = {
|
|
144
|
+
"\x1b[A": "up",
|
|
145
|
+
"\x1bOA": "up",
|
|
146
|
+
"\x1b[B": "down",
|
|
147
|
+
"\x1bOB": "down",
|
|
148
|
+
"\x1b[C": "right",
|
|
149
|
+
"\x1bOC": "right",
|
|
150
|
+
"\x1b[D": "left",
|
|
151
|
+
"\x1bOD": "left",
|
|
152
|
+
h: "left",
|
|
153
|
+
j: "down",
|
|
154
|
+
k: "up",
|
|
155
|
+
l: "right",
|
|
156
|
+
}[input]
|
|
157
|
+
if (key) void onKey(key)
|
|
158
|
+
else if (input === "+" || input === "=") void onKey("zoom-in")
|
|
159
|
+
else if (input === "-") void onKey("zoom-out")
|
|
160
|
+
else if (input === "0") void onKey("reset")
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
process.once("SIGINT", () => void finish())
|
|
165
|
+
process.once("SIGTERM", () => void finish())
|
|
166
|
+
process.once("SIGHUP", () => void finish())
|
|
167
|
+
|
|
168
|
+
if (!pngPath) {
|
|
169
|
+
process.stdout.write("Mermaid image path is missing. Press Enter to close.\n")
|
|
170
|
+
waitForClose(() => {})
|
|
171
|
+
} else {
|
|
172
|
+
try {
|
|
173
|
+
const base64Data = (await readFile(pngPath)).toString("base64")
|
|
174
|
+
const dimensions = getPngDimensions(base64Data)
|
|
175
|
+
if (!dimensions) throw new Error("invalid PNG")
|
|
176
|
+
|
|
177
|
+
const columns = process.stdout.columns ?? 160
|
|
178
|
+
const rows = process.stdout.rows ?? 80
|
|
179
|
+
const availableWidth = Math.max(1, columns - 2)
|
|
180
|
+
const availableHeight = Math.max(1, rows - 4)
|
|
181
|
+
const graphicsInfo = await herdrRequest("pane.graphics.info", {
|
|
182
|
+
pane_id: herdrPaneId,
|
|
183
|
+
})
|
|
184
|
+
const cellDimensions = {
|
|
185
|
+
widthPx: graphicsInfo.cell_width_px ?? 9,
|
|
186
|
+
heightPx: graphicsInfo.cell_height_px ?? 18,
|
|
187
|
+
}
|
|
188
|
+
const imageId = allocateImageId()
|
|
189
|
+
let zoomIndex = 0
|
|
190
|
+
let panX = 0
|
|
191
|
+
let panY = 0
|
|
192
|
+
let imageColumns = 0
|
|
193
|
+
let imageRows = 0
|
|
194
|
+
let maxPanX = 0
|
|
195
|
+
let maxPanY = 0
|
|
196
|
+
let renderQueue = Promise.resolve()
|
|
197
|
+
|
|
198
|
+
const clearGraphics = async () => {
|
|
199
|
+
process.stdout.write(deleteKittyImage(imageId))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function renderFrame() {
|
|
203
|
+
const size = calculateImageCellSize(
|
|
204
|
+
dimensions,
|
|
205
|
+
availableWidth * zoomSteps[zoomIndex],
|
|
206
|
+
availableHeight * zoomSteps[zoomIndex],
|
|
207
|
+
cellDimensions,
|
|
208
|
+
)
|
|
209
|
+
imageColumns = size.columns
|
|
210
|
+
imageRows = size.rows
|
|
211
|
+
maxPanX = Math.max(0, imageColumns - availableWidth)
|
|
212
|
+
maxPanY = Math.max(0, imageRows - availableHeight)
|
|
213
|
+
panX = Math.max(-maxPanX, Math.min(0, panX))
|
|
214
|
+
panY = Math.max(-maxPanY, Math.min(0, panY))
|
|
215
|
+
|
|
216
|
+
const visibleColumns = Math.min(imageColumns, availableWidth)
|
|
217
|
+
const visibleRows = Math.min(imageRows, availableHeight)
|
|
218
|
+
const sourceX = Math.min(
|
|
219
|
+
dimensions.widthPx - 1,
|
|
220
|
+
Math.floor((-panX * cellDimensions.widthPx) / size.scale),
|
|
221
|
+
)
|
|
222
|
+
const sourceY = Math.min(
|
|
223
|
+
dimensions.heightPx - 1,
|
|
224
|
+
Math.floor((-panY * cellDimensions.heightPx) / size.scale),
|
|
225
|
+
)
|
|
226
|
+
const sourceWidth = Math.min(
|
|
227
|
+
dimensions.widthPx - sourceX,
|
|
228
|
+
Math.ceil((visibleColumns * cellDimensions.widthPx) / size.scale),
|
|
229
|
+
)
|
|
230
|
+
const sourceHeight = Math.min(
|
|
231
|
+
dimensions.heightPx - sourceY,
|
|
232
|
+
Math.ceil((visibleRows * cellDimensions.heightPx) / size.scale),
|
|
233
|
+
)
|
|
234
|
+
const imageTop =
|
|
235
|
+
imageRows <= availableHeight
|
|
236
|
+
? Math.floor((availableHeight - imageRows) / 2)
|
|
237
|
+
: 0
|
|
238
|
+
const imageLeft =
|
|
239
|
+
imageColumns <= availableWidth
|
|
240
|
+
? Math.floor((columns - imageColumns) / 2)
|
|
241
|
+
: 0
|
|
242
|
+
|
|
243
|
+
const frame = encodeKittyFrame(
|
|
244
|
+
base64Data,
|
|
245
|
+
imageId,
|
|
246
|
+
visibleColumns,
|
|
247
|
+
visibleRows,
|
|
248
|
+
sourceX,
|
|
249
|
+
sourceY,
|
|
250
|
+
sourceWidth,
|
|
251
|
+
sourceHeight,
|
|
252
|
+
)
|
|
253
|
+
const hint = `Zoom +/- · Pan hjkl/arrows · Reset 0 · Close Enter/Esc`
|
|
254
|
+
const hintLeft = Math.max(0, Math.floor((columns - hint.length) / 2))
|
|
255
|
+
process.stdout.write(
|
|
256
|
+
`\x1b[2J\x1b[H\x1b[${imageTop + 1};${imageLeft + 1}H${frame}\x1b[${rows - 1};${hintLeft + 1}H${hint}`,
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function scheduleRender() {
|
|
261
|
+
renderQueue = renderQueue.then(renderFrame)
|
|
262
|
+
void renderQueue.catch(() => void finish())
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function pan(deltaX, deltaY) {
|
|
266
|
+
const nextX = Math.max(-maxPanX, Math.min(0, panX + deltaX))
|
|
267
|
+
const nextY = Math.max(-maxPanY, Math.min(0, panY + deltaY))
|
|
268
|
+
if (nextX === panX && nextY === panY) return
|
|
269
|
+
panX = nextX
|
|
270
|
+
panY = nextY
|
|
271
|
+
scheduleRender()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function handleKey(key) {
|
|
275
|
+
if (key === "up") pan(0, panStep)
|
|
276
|
+
else if (key === "down") pan(0, -panStep)
|
|
277
|
+
else if (key === "left") pan(panStep, 0)
|
|
278
|
+
else if (key === "right") pan(-panStep, 0)
|
|
279
|
+
else if (key === "zoom-in" && zoomIndex < zoomSteps.length - 1) {
|
|
280
|
+
zoomIndex++
|
|
281
|
+
panX = 0
|
|
282
|
+
panY = 0
|
|
283
|
+
scheduleRender()
|
|
284
|
+
} else if (key === "zoom-out" && zoomIndex > 0) {
|
|
285
|
+
zoomIndex--
|
|
286
|
+
panX = 0
|
|
287
|
+
panY = 0
|
|
288
|
+
scheduleRender()
|
|
289
|
+
} else if (key === "reset") {
|
|
290
|
+
zoomIndex = 0
|
|
291
|
+
panX = 0
|
|
292
|
+
panY = 0
|
|
293
|
+
scheduleRender()
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
await renderFrame()
|
|
298
|
+
waitForClose(handleKey, clearGraphics)
|
|
299
|
+
} catch (error) {
|
|
300
|
+
process.stdout.write(
|
|
301
|
+
`Could not display Mermaid PNG: ${error instanceof Error ? error.message : String(error)}\nPress Enter to close.\n`,
|
|
302
|
+
)
|
|
303
|
+
waitForClose(() => {})
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herbertgao/pi-mermaid-open",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Find skipped Mermaid diagrams and show them in a terminal image viewer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"diagram",
|
|
7
7
|
"mermaid",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"src/**/*.ts",
|
|
26
|
+
"herdr-plugin/**/*",
|
|
26
27
|
"README.md",
|
|
27
28
|
"CHANGELOG.md",
|
|
28
29
|
"LICENSE"
|
|
@@ -32,6 +33,16 @@
|
|
|
32
33
|
"access": "public",
|
|
33
34
|
"provenance": true
|
|
34
35
|
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "node --experimental-strip-types --test tests/**/*.test.ts"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"grok-mermaid": "0.2.2"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@earendil-works/pi-coding-agent": ">=0.84.0",
|
|
44
|
+
"@earendil-works/pi-tui": ">=0.84.0"
|
|
45
|
+
},
|
|
35
46
|
"engines": {
|
|
36
47
|
"node": ">=20"
|
|
37
48
|
},
|
|
@@ -42,8 +53,8 @@
|
|
|
42
53
|
},
|
|
43
54
|
"x-upstream": {
|
|
44
55
|
"package": "@tifan/pi-mermaid-open",
|
|
45
|
-
"version": "0.
|
|
56
|
+
"version": "0.2.0",
|
|
46
57
|
"repository": "https://github.com/tifandotme/pi-extensions",
|
|
47
|
-
"commit": "
|
|
58
|
+
"commit": "b39d1e6359c78718cbf8fe5c74c82b89065e9a0b"
|
|
48
59
|
}
|
|
49
60
|
}
|