Run prettier on .pi folder

This commit is contained in:
Claudio Ortolina
2026-05-04 21:21:10 +01:00
parent 286d6fecf3
commit 7bf89d9a1d
8 changed files with 272 additions and 191 deletions
+12 -4
View File
@@ -35,7 +35,7 @@ export default function (pi: ExtensionAPI) {
// Proactively tell the agent which paths are off-limits so it doesn't try in the first place // Proactively tell the agent which paths are off-limits so it doesn't try in the first place
pi.on("before_agent_start", async (event, _ctx) => { pi.on("before_agent_start", async (event, _ctx) => {
if (config.blocked_paths.length === 0) return; if (config.blocked_paths.length === 0) return;
const pathList = config.blocked_paths.map((p) => ` - \`${p}\``).join('\n'); const pathList = config.blocked_paths.map((p) => ` - \`${p}\``).join("\n");
return { return {
systemPrompt: systemPrompt:
event.systemPrompt + event.systemPrompt +
@@ -63,7 +63,8 @@ export default function (pi: ExtensionAPI) {
`🚫 ACCESS DENIED by sensitive-file-guard: paths matching "${match}" are permanently protected. ` + `🚫 ACCESS DENIED by sensitive-file-guard: paths matching "${match}" are permanently protected. ` +
`DO NOT retry with other tools (bash, find, ls, grep, etc.). ` + `DO NOT retry with other tools (bash, find, ls, grep, etc.). ` +
`Tell the user the file is protected and STOP.`; `Tell the user the file is protected and STOP.`;
if (ctx.hasUI) ctx.ui.notify(`Blocked sensitive path: ${path}`, "warning"); if (ctx.hasUI)
ctx.ui.notify(`Blocked sensitive path: ${path}`, "warning");
return { block: true, reason }; return { block: true, reason };
} }
return; return;
@@ -79,14 +80,21 @@ export default function (pi: ExtensionAPI) {
// Rough substring match: does the pattern text (with glob stripped) // Rough substring match: does the pattern text (with glob stripped)
// appear in the command? // appear in the command?
const literal = p.replace(/[*?]/g, ""); const literal = p.replace(/[*?]/g, "");
return literal.length > 0 && command.toLowerCase().includes(literal.toLowerCase()); return (
literal.length > 0 &&
command.toLowerCase().includes(literal.toLowerCase())
);
}); });
if (pathHit) { if (pathHit) {
const reason = const reason =
`🚫 ACCESS DENIED by sensitive-file-guard: commands targeting "${pathHit}" are permanently blocked. ` + `🚫 ACCESS DENIED by sensitive-file-guard: commands targeting "${pathHit}" are permanently blocked. ` +
`DO NOT retry with alternative commands. Tell the user the path is protected and STOP.`; `DO NOT retry with alternative commands. Tell the user the path is protected and STOP.`;
if (ctx.hasUI) ctx.ui.notify(`Blocked sensitive path in bash: ${pathHit}`, "warning"); if (ctx.hasUI)
ctx.ui.notify(
`Blocked sensitive path in bash: ${pathHit}`,
"warning",
);
return { block: true, reason }; return { block: true, reason };
} }
+98 -46
View File
@@ -23,11 +23,7 @@ import {
DEFAULT_MAX_LINES, DEFAULT_MAX_LINES,
} from "@mariozechner/pi-coding-agent"; } from "@mariozechner/pi-coding-agent";
import { BorderedLoader } from "@mariozechner/pi-coding-agent"; import { BorderedLoader } from "@mariozechner/pi-coding-agent";
import { import { matchesKey, Key, truncateToWidth } from "@mariozechner/pi-tui";
matchesKey,
Key,
truncateToWidth,
} from "@mariozechner/pi-tui";
import { Type } from "typebox"; import { Type } from "typebox";
// ── Types ─────────────────────────────────────────────────────────────────── // ── Types ───────────────────────────────────────────────────────────────────
@@ -275,7 +271,10 @@ async function fetchErrors(
token: string, token: string,
signal?: AbortSignal, signal?: AbortSignal,
): Promise<ErrorListResponse> { ): Promise<ErrorListResponse> {
return fetchApi(url, token, (data) => { return fetchApi(
url,
token,
(data) => {
const obj = data as Record<string, unknown>; const obj = data as Record<string, unknown>;
if (!obj.errors || !Array.isArray(obj.errors)) { if (!obj.errors || !Array.isArray(obj.errors)) {
throw new Error( throw new Error(
@@ -285,7 +284,9 @@ async function fetchErrors(
); );
} }
return data as ErrorListResponse; return data as ErrorListResponse;
}, signal); },
signal,
);
} }
async function fetchError( async function fetchError(
@@ -293,7 +294,10 @@ async function fetchError(
token: string, token: string,
signal?: AbortSignal, signal?: AbortSignal,
): Promise<ErrorDetailResponse> { ): Promise<ErrorDetailResponse> {
return fetchApi(url, token, (data) => { return fetchApi(
url,
token,
(data) => {
const obj = data as Record<string, unknown>; const obj = data as Record<string, unknown>;
if (!obj.error || typeof obj.error !== "object") { if (!obj.error || typeof obj.error !== "object") {
throw new Error( throw new Error(
@@ -303,7 +307,9 @@ async function fetchError(
); );
} }
return data as ErrorDetailResponse; return data as ErrorDetailResponse;
}, signal); },
signal,
);
} }
// ── TUI Theme ─────────────────────────────────────────────────────────────── // ── TUI Theme ───────────────────────────────────────────────────────────────
@@ -378,7 +384,10 @@ class ErrorBrowser {
private readonly baseUrl: string; private readonly baseUrl: string;
private readonly token: string; private readonly token: string;
private readonly requestRender: () => void; private readonly requestRender: () => void;
private readonly notify: (msg: string, level: "error" | "info" | "warning") => void; private readonly notify: (
msg: string,
level: "error" | "info" | "warning",
) => void;
constructor( constructor(
errors: ErrorListItem[], errors: ErrorListItem[],
@@ -411,7 +420,10 @@ class ErrorBrowser {
if (this.errors.length === 0) { if (this.errors.length === 0) {
this.cursorIndex = -1; this.cursorIndex = -1;
} else { } else {
this.cursorIndex = Math.max(0, Math.min(this.cursorIndex, this.errors.length - 1)); this.cursorIndex = Math.max(
0,
Math.min(this.cursorIndex, this.errors.length - 1),
);
} }
} }
@@ -423,7 +435,10 @@ class ErrorBrowser {
} else if (this.cursorIndex >= this.scrollOffset + maxEntries) { } else if (this.cursorIndex >= this.scrollOffset + maxEntries) {
this.scrollOffset = this.cursorIndex - maxEntries + 1; this.scrollOffset = this.cursorIndex - maxEntries + 1;
} }
this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, this.errors.length - 1)); this.scrollOffset = Math.max(
0,
Math.min(this.scrollOffset, this.errors.length - 1),
);
} }
// ── HTTP helpers ─────────────────────────────────────────────────────── // ── HTTP helpers ───────────────────────────────────────────────────────
@@ -471,7 +486,11 @@ class ErrorBrowser {
this.invalidate(); this.invalidate();
this.requestRender(); this.requestRender();
const url = buildUrl(this.baseUrl, "/api/v1/errors", this.buildQueryParams()); const url = buildUrl(
this.baseUrl,
"/api/v1/errors",
this.buildQueryParams(),
);
const controller = new AbortController(); const controller = new AbortController();
this.currentAbortController = controller; this.currentAbortController = controller;
@@ -508,7 +527,11 @@ class ErrorBrowser {
this.invalidate(); this.invalidate();
this.requestRender(); this.requestRender();
const url = buildUrl(this.baseUrl, "/api/v1/errors", this.buildQueryParams()); const url = buildUrl(
this.baseUrl,
"/api/v1/errors",
this.buildQueryParams(),
);
const controller = new AbortController(); const controller = new AbortController();
this.currentAbortController = controller; this.currentAbortController = controller;
@@ -638,7 +661,10 @@ class ErrorBrowser {
const pageSize = Math.floor(this.visibleHeight / this.errorEntryLines); const pageSize = Math.floor(this.visibleHeight / this.errorEntryLines);
this.cursorIndex -= pageSize; this.cursorIndex -= pageSize;
moved = true; moved = true;
} else if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("f"))) { } else if (
matchesKey(data, Key.pageDown) ||
matchesKey(data, Key.ctrl("f"))
) {
const pageSize = Math.floor(this.visibleHeight / this.errorEntryLines); const pageSize = Math.floor(this.visibleHeight / this.errorEntryLines);
this.cursorIndex += pageSize; this.cursorIndex += pageSize;
moved = true; moved = true;
@@ -689,7 +715,10 @@ class ErrorBrowser {
} else if (matchesKey(data, Key.pageUp)) { } else if (matchesKey(data, Key.pageUp)) {
this.detailScrollOffset -= this.visibleHeight; this.detailScrollOffset -= this.visibleHeight;
moved = true; moved = true;
} else if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("f"))) { } else if (
matchesKey(data, Key.pageDown) ||
matchesKey(data, Key.ctrl("f"))
) {
this.detailScrollOffset += this.visibleHeight; this.detailScrollOffset += this.visibleHeight;
moved = true; moved = true;
} else if (matchesKey(data, Key.ctrl("b"))) { } else if (matchesKey(data, Key.ctrl("b"))) {
@@ -699,13 +728,19 @@ class ErrorBrowser {
this.detailScrollOffset = 0; this.detailScrollOffset = 0;
moved = true; moved = true;
} else if (matchesKey(data, Key.end) || matchesKey(data, "G")) { } else if (matchesKey(data, Key.end) || matchesKey(data, "G")) {
this.detailScrollOffset = Math.max(0, this.buildDetailLines().length - 1); this.detailScrollOffset = Math.max(
0,
this.buildDetailLines().length - 1,
);
moved = true; moved = true;
} }
if (moved) { if (moved) {
const maxOffset = Math.max(0, this.buildDetailLines().length - 1); const maxOffset = Math.max(0, this.buildDetailLines().length - 1);
this.detailScrollOffset = Math.max(0, Math.min(this.detailScrollOffset, maxOffset)); this.detailScrollOffset = Math.max(
0,
Math.min(this.detailScrollOffset, maxOffset),
);
this.invalidate(); this.invalidate();
} }
@@ -746,7 +781,9 @@ class ErrorBrowser {
for (let i = 1; i < viewportRows - 1; i++) { for (let i = 1; i < viewportRows - 1; i++) {
if (i === mid) { if (i === mid) {
result.push(truncateToWidth(theme.fg("muted", " Loading…"), width, "")); result.push(
truncateToWidth(theme.fg("muted", " Loading…"), width, ""),
);
} else { } else {
result.push(""); result.push("");
} }
@@ -809,12 +846,24 @@ class ErrorBrowser {
else if (error.status === "resolved") statusColor = "success"; else if (error.status === "resolved") statusColor = "success";
const mutedLabel = error.muted ? " [MUTED]" : ""; const mutedLabel = error.muted ? " [MUTED]" : "";
const statusBadge = theme.fg(statusColor, `[${error.status.toUpperCase()}]`); const statusBadge = theme.fg(
statusColor,
`[${error.status.toUpperCase()}]`,
);
// Line 1: cursor + status badge + kind + truncated reason // Line 1: cursor + status badge + kind + truncated reason
const reasonText = truncateReason(error.reason, Math.max(30, width - 25)); const reasonText = truncateReason(
error.reason,
Math.max(30, width - 25),
);
const line1 = `${cursor} ${statusBadge}${mutedLabel} ${error.kind}: ${reasonText}`; const line1 = `${cursor} ${statusBadge}${mutedLabel} ${error.kind}: ${reasonText}`;
result.push(truncateToWidth(isCursor ? theme.fg("accent", line1) : line1, width, "")); result.push(
truncateToWidth(
isCursor ? theme.fg("accent", line1) : line1,
width,
"",
),
);
// Line 2: source info // Line 2: source info
const sourceInfo = error.source_function const sourceInfo = error.source_function
@@ -857,9 +906,7 @@ class ErrorBrowser {
truncateToWidth( truncateToWidth(
theme.fg( theme.fg(
"dim", "dim",
hasMore hasMore ? ` l load more q quit` : ` — end of results — q quit`,
? ` l load more q quit`
: ` — end of results — q quit`,
), ),
width, width,
"", "",
@@ -972,13 +1019,13 @@ export default function prodErrorsExtension(pi: ExtensionAPI) {
parameters: Type.Object({ parameters: Type.Object({
status: Type.Optional( status: Type.Optional(
Type.Union([Type.Literal("resolved"), Type.Literal("unresolved")], { Type.Union([Type.Literal("resolved"), Type.Literal("unresolved")], {
description: description: "Filter by error status: 'resolved' or 'unresolved'.",
"Filter by error status: 'resolved' or 'unresolved'.",
}), }),
), ),
muted: Type.Optional( muted: Type.Optional(
Type.Boolean({ Type.Boolean({
description: "Filter by muted state. true = only muted, false = only unmuted.", description:
"Filter by muted state. true = only muted, false = only unmuted.",
}), }),
), ),
search: Type.Optional( search: Type.Optional(
@@ -1000,13 +1047,7 @@ export default function prodErrorsExtension(pi: ExtensionAPI) {
}), }),
), ),
}), }),
async execute( async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
_toolCallId,
params,
signal,
_onUpdate,
_ctx,
) {
// Early abort check // Early abort check
if (signal?.aborted) { if (signal?.aborted) {
return { content: [{ type: "text", text: "Cancelled" }] }; return { content: [{ type: "text", text: "Cancelled" }] };
@@ -1043,7 +1084,11 @@ export default function prodErrorsExtension(pi: ExtensionAPI) {
if (params.muted !== undefined && params.muted !== null) { if (params.muted !== undefined && params.muted !== null) {
queryParams.muted = String(params.muted); queryParams.muted = String(params.muted);
} }
if (params.search !== undefined && params.search !== null && params.search !== "") { if (
params.search !== undefined &&
params.search !== null &&
params.search !== ""
) {
queryParams.search = params.search; queryParams.search = params.search;
} }
if (params.limit !== undefined && params.limit !== null) { if (params.limit !== undefined && params.limit !== null) {
@@ -1075,8 +1120,18 @@ export default function prodErrorsExtension(pi: ExtensionAPI) {
// Handle empty results // Handle empty results
if (data.errors.length === 0) { if (data.errors.length === 0) {
return { return {
content: [{ type: "text", text: "No errors found matching the given filters." }], content: [
details: { total: data.total, count: 0, offset: data.offset, limit: data.limit }, {
type: "text",
text: "No errors found matching the given filters.",
},
],
details: {
total: data.total,
count: 0,
offset: data.offset,
limit: data.limit,
},
}; };
} }
@@ -1126,13 +1181,7 @@ export default function prodErrorsExtension(pi: ExtensionAPI) {
"The error ID (integer) to fetch full details for. Get this from fetch_production_errors.", "The error ID (integer) to fetch full details for. Get this from fetch_production_errors.",
}), }),
}), }),
async execute( async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
_toolCallId,
params,
signal,
_onUpdate,
_ctx,
) {
// Early abort check // Early abort check
if (signal?.aborted) { if (signal?.aborted) {
return { content: [{ type: "text", text: "Cancelled" }] }; return { content: [{ type: "text", text: "Cancelled" }] };
@@ -1256,7 +1305,10 @@ export default function prodErrorsExtension(pi: ExtensionAPI) {
); );
if (errorsData === null) { if (errorsData === null) {
ctx.ui.notify("Cancelled or fetch failed — check logs for details", "info"); ctx.ui.notify(
"Cancelled or fetch failed — check logs for details",
"info",
);
return; return;
} }
+50 -33
View File
@@ -21,11 +21,7 @@ import {
DEFAULT_MAX_LINES, DEFAULT_MAX_LINES,
} from "@mariozechner/pi-coding-agent"; } from "@mariozechner/pi-coding-agent";
import { Type } from "typebox"; import { Type } from "typebox";
import { import { matchesKey, Key, truncateToWidth } from "@mariozechner/pi-tui";
matchesKey,
Key,
truncateToWidth,
} from "@mariozechner/pi-tui";
// ── Types ─────────────────────────────────────────────────────────────────── // ── Types ───────────────────────────────────────────────────────────────────
@@ -80,7 +76,10 @@ class LogViewer {
this.visualMode = false; this.visualMode = false;
this.visualAnchor = 0; this.visualAnchor = 0;
this.scrollOffset = 0; this.scrollOffset = 0;
this.visibleHeight = Math.min(lines.length, Math.max(10, process.stdout.rows - chromeHeight)); this.visibleHeight = Math.min(
lines.length,
Math.max(10, process.stdout.rows - chromeHeight),
);
this.clampCursor(); this.clampCursor();
this.clampViewport(); this.clampViewport();
this.invalidate(); this.invalidate();
@@ -101,7 +100,10 @@ class LogViewer {
if (this.lines.length === 0) { if (this.lines.length === 0) {
this.cursorIndex = -1; this.cursorIndex = -1;
} else { } else {
this.cursorIndex = Math.max(0, Math.min(this.cursorIndex, this.lines.length - 1)); this.cursorIndex = Math.max(
0,
Math.min(this.cursorIndex, this.lines.length - 1),
);
} }
} }
@@ -129,7 +131,10 @@ class LogViewer {
} }
// Escape or q in normal mode closes the viewer // Escape or q in normal mode closes the viewer
if (!this.visualMode && (matchesKey(data, Key.escape) || matchesKey(data, "q"))) { if (
!this.visualMode &&
(matchesKey(data, Key.escape) || matchesKey(data, "q"))
) {
this.onClose?.(); this.onClose?.();
return; return;
} }
@@ -156,7 +161,10 @@ class LogViewer {
const start = Math.min(this.visualAnchor, this.cursorIndex); const start = Math.min(this.visualAnchor, this.cursorIndex);
const end = Math.max(this.visualAnchor, this.cursorIndex); const end = Math.max(this.visualAnchor, this.cursorIndex);
// this.lines[0] = newest, so reverse the slice for oldest-first // this.lines[0] = newest, so reverse the slice for oldest-first
const text = this.lines.slice(start, end + 1).reverse().join("\n"); const text = this.lines
.slice(start, end + 1)
.reverse()
.join("\n");
this.onCopy?.(text); this.onCopy?.(text);
} }
return; return;
@@ -184,7 +192,10 @@ class LogViewer {
} else if (matchesKey(data, Key.pageUp)) { } else if (matchesKey(data, Key.pageUp)) {
this.cursorIndex -= this.visibleHeight; this.cursorIndex -= this.visibleHeight;
moved = true; moved = true;
} else if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("f"))) { } else if (
matchesKey(data, Key.pageDown) ||
matchesKey(data, Key.ctrl("f"))
) {
this.cursorIndex += this.visibleHeight; this.cursorIndex += this.visibleHeight;
moved = true; moved = true;
} else if (matchesKey(data, Key.ctrl("b"))) { } else if (matchesKey(data, Key.ctrl("b"))) {
@@ -237,9 +248,8 @@ class LogViewer {
const basePrefix = ` ${numStr}`; const basePrefix = ` ${numStr}`;
const isCursor = absIdx === this.cursorIndex; const isCursor = absIdx === this.cursorIndex;
const isSelected = this.visualMode const isSelected =
&& absIdx >= selStart this.visualMode && absIdx >= selStart && absIdx <= selEnd;
&& absIdx <= selEnd;
if (isCursor) { if (isCursor) {
// Cursor takes priority over selection highlight // Cursor takes priority over selection highlight
@@ -349,13 +359,7 @@ export default function prodLogsExtension(pi: ExtensionAPI) {
}), }),
), ),
}), }),
async execute( async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
_toolCallId,
params,
signal,
_onUpdate,
_ctx,
) {
// Early abort check // Early abort check
if (signal?.aborted) { if (signal?.aborted) {
return { content: [{ type: "text", text: "Cancelled" }] }; return { content: [{ type: "text", text: "Cancelled" }] };
@@ -498,7 +502,10 @@ export default function prodLogsExtension(pi: ExtensionAPI) {
); );
if (logLines === null) { if (logLines === null) {
ctx.ui.notify("Cancelled or fetch failed — check logs for details", "info"); ctx.ui.notify(
"Cancelled or fetch failed — check logs for details",
"info",
);
return; return;
} }
@@ -519,7 +526,10 @@ export default function prodLogsExtension(pi: ExtensionAPI) {
let loading = false; let loading = false;
const visibleLogLines = () => const visibleLogLines = () =>
Math.min(logLinesMut.length, Math.max(10, process.stdout.rows - chromeHeight)); Math.min(
logLinesMut.length,
Math.max(10, process.stdout.rows - chromeHeight),
);
const viewer = new LogViewer(logLinesMut, visibleLogLines()); const viewer = new LogViewer(logLinesMut, visibleLogLines());
@@ -553,7 +563,8 @@ export default function prodLogsExtension(pi: ExtensionAPI) {
return { return {
render(width: number) { render(width: number) {
const pct = viewer.total > 0 const pct =
viewer.total > 0
? Math.round((viewer.offset / viewer.total) * 100) ? Math.round((viewer.offset / viewer.total) * 100)
: 0; : 0;
const from = viewer.offset + 1; const from = viewer.offset + 1;
@@ -565,20 +576,28 @@ export default function prodLogsExtension(pi: ExtensionAPI) {
const result: string[] = []; const result: string[] = [];
const border = theme.fg("accent", "─".repeat(width)); const border = theme.fg("accent", "─".repeat(width));
result.push(border); result.push(border);
result.push(truncateToWidth( result.push(
truncateToWidth(
theme.fg("accent", theme.bold(" Production Logs")), theme.fg("accent", theme.bold(" Production Logs")),
width, width,
"", "",
)); ),
);
const countText = loading const countText = loading
? " Refreshing..." ? " Refreshing..."
: ` ${logLinesMut.length} lines`; : ` ${logLinesMut.length} lines`;
result.push(truncateToWidth(theme.fg("muted", countText), width, "")); result.push(
result.push(truncateToWidth(theme.fg("dim", "─".repeat(40)), width, "")); truncateToWidth(theme.fg("muted", countText), width, ""),
);
result.push(
truncateToWidth(theme.fg("dim", "─".repeat(40)), width, ""),
);
const infoText = ` Lines ${from}-${to} of ${logLinesMut.length} (${pct}%)`; const infoText = ` Lines ${from}-${to} of ${logLinesMut.length} (${pct}%)`;
result.push(truncateToWidth(theme.fg("dim", infoText), width, "")); result.push(
truncateToWidth(theme.fg("dim", infoText), width, ""),
);
// Log lines // Log lines
const logRendered = viewer.render(width, theme); const logRendered = viewer.render(width, theme);
@@ -590,11 +609,9 @@ export default function prodLogsExtension(pi: ExtensionAPI) {
: viewer.visualMode : viewer.visualMode
? " VISUAL: j/k extend · y copy · Esc cancel · Enter copy line" ? " VISUAL: j/k extend · y copy · Esc cancel · Enter copy line"
: " ↑↓/jk scroll · PgUp/PgDn page · Home/End jump · v visual · Enter copy line · r refresh · Esc close"; : " ↑↓/jk scroll · PgUp/PgDn page · Home/End jump · v visual · Enter copy line · r refresh · Esc close";
result.push(truncateToWidth( result.push(
theme.fg("dim", helpLine), truncateToWidth(theme.fg("dim", helpLine), width, ""),
width, );
"",
));
result.push(border); result.push(border);
return result; return result;
+22 -11
View File
@@ -33,7 +33,10 @@ const S3_REGION = "nbg1";
function humanSize(bytes: number): string { function humanSize(bytes: number): string {
if (bytes === 0) return "0 B"; if (bytes === 0) return "0 B";
const units = ["B", "KB", "MB", "GB", "TB"]; const units = ["B", "KB", "MB", "GB", "TB"];
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1); const i = Math.min(
Math.floor(Math.log(bytes) / Math.log(1024)),
units.length - 1,
);
return `${(bytes / 1024 ** i).toFixed(1)} ${units[i]}`; return `${(bytes / 1024 ** i).toFixed(1)} ${units[i]}`;
} }
@@ -152,7 +155,10 @@ export default function s3BrowserExtension(pi: ExtensionAPI) {
); );
if (objects === null) { if (objects === null) {
ctx.ui.notify("Cancelled or fetch failed — check logs for details", "info"); ctx.ui.notify(
"Cancelled or fetch failed — check logs for details",
"info",
);
return; return;
} }
@@ -174,7 +180,8 @@ export default function s3BrowserExtension(pi: ExtensionAPI) {
// Summary header line // Summary header line
const summary = `${objects.length} files · ${humanSize(totalSize)} total`; const summary = `${objects.length} files · ${humanSize(totalSize)} total`;
const selectedKey = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => { const selectedKey = await ctx.ui.custom<string | null>(
(tui, theme, _kb, done) => {
const container = new Container(); const container = new Container();
// Top border // Top border
@@ -184,16 +191,19 @@ export default function s3BrowserExtension(pi: ExtensionAPI) {
// Header // Header
container.addChild( container.addChild(
new Text(theme.fg("accent", theme.bold("S3 Backups — ffmusiclibrary/prod/")), 1, 0), new Text(
); theme.fg(
container.addChild( "accent",
new Text(theme.fg("muted", summary), 1, 0), theme.bold("S3 Backups — ffmusiclibrary/prod/"),
),
1,
0,
),
); );
container.addChild(new Text(theme.fg("muted", summary), 1, 0));
// Spacer before list // Spacer before list
container.addChild( container.addChild(new Text(theme.fg("dim", "─".repeat(40)), 1, 0));
new Text(theme.fg("dim", "─".repeat(40)), 1, 0),
);
// SelectList // SelectList
const selectList = new SelectList(items, Math.min(items.length, 20), { const selectList = new SelectList(items, Math.min(items.length, 20), {
@@ -233,7 +243,8 @@ export default function s3BrowserExtension(pi: ExtensionAPI) {
tui.requestRender(); tui.requestRender();
}, },
}; };
}); },
);
if (selectedKey) { if (selectedKey) {
const obj = objects.find((o) => o.key === selectedKey); const obj = objects.find((o) => o.key === selectedKey);
+1
View File
@@ -1,6 +1,7 @@
--- ---
description: Create a new backlog task description: Create a new backlog task
--- ---
Let's create a new backlog task via a predefined process. **IT'S CRITICAL YOU FOLLOW THIS PROCESS**. Let's create a new backlog task via a predefined process. **IT'S CRITICAL YOU FOLLOW THIS PROCESS**.
To start, ask me for: To start, ask me for:
+1 -5
View File
@@ -13,9 +13,5 @@
".gnupg/", ".gnupg/",
"~/.pi/agent/sessions/" "~/.pi/agent/sessions/"
], ],
"blocked_commands": [ "blocked_commands": ["printenv", "\\benv\\b", "\\bset\\b"]
"printenv",
"\\benv\\b",
"\\bset\\b"
]
} }
+2 -6
View File
@@ -1,8 +1,4 @@
{ {
"skills": [ "skills": ["../.claude/skills"],
"../.claude/skills" "packages": ["npm:pi-mcp-adapter"]
],
"packages": [
"npm:pi-mcp-adapter"
]
} }