Commit 012ac0ac1a06
Changed files (2)
dots
pi
agent
extensions
defaults
dots/pi/agent/extensions/defaults/index.ts
@@ -0,0 +1,142 @@
+/**
+ * Defaults Extension for Pi
+ *
+ * Sensible quality-of-life improvements:
+ * - Directory-aware read (returns listing instead of EISDIR error)
+ * - get_current_time tool (structured date/time for the LLM)
+ * - Git rebase helper (injects editor env vars to prevent hangs)
+ *
+ * Adapted from: aliou/pi-extensions defaults
+ */
+
+import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import { Type } from "@sinclair/typebox";
+import { lstat } from "node:fs/promises";
+import { resolve } from "node:path";
+import { createReadTool, createLsTool } from "@mariozechner/pi-coding-agent";
+
+export default function (pi: ExtensionAPI) {
+ // ── Directory-aware read ──────────────────────────────────
+
+ const cwd = process.cwd();
+ const nativeRead = createReadTool(cwd);
+ const nativeLs = createLsTool(cwd);
+
+ pi.registerTool({
+ ...nativeRead,
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
+ const { path } = params as { path: string; offset?: number; limit?: number };
+ const absolutePath = resolve(ctx.cwd, path);
+
+ try {
+ const stat = await lstat(absolutePath);
+ if (stat.isDirectory()) {
+ return nativeLs.execute(toolCallId, { path }, signal, onUpdate);
+ }
+ } catch {
+ // Let nativeRead handle the error
+ }
+
+ return nativeRead.execute(
+ toolCallId,
+ params as { path: string; offset?: number; limit?: number },
+ signal,
+ onUpdate,
+ );
+ },
+ });
+
+ // ── get_current_time tool ─────────────────────────────────
+
+ const GetCurrentTimeParams = Type.Object({
+ format: Type.Optional(
+ Type.String({
+ description: "Output format: 'iso8601' (default), 'unix', 'date', 'time'",
+ }),
+ ),
+ });
+
+ pi.registerTool({
+ name: "get_current_time",
+ label: "Get Current Time",
+ description:
+ "Get the current date and time. Returns formatted time with date, time, timezone, and day of week.",
+ parameters: GetCurrentTimeParams,
+
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
+ const now = new Date();
+ const format = (params as { format?: string }).format || "iso8601";
+
+ let formatted: string;
+ switch (format.toLowerCase()) {
+ case "unix":
+ formatted = Math.floor(now.getTime() / 1000).toString();
+ break;
+ case "date":
+ formatted = now.toLocaleDateString();
+ break;
+ case "time":
+ formatted = now.toLocaleTimeString();
+ break;
+ default:
+ formatted = now.toISOString();
+ }
+
+ const timezoneOffset = -now.getTimezoneOffset();
+ const offsetHours = Math.floor(Math.abs(timezoneOffset) / 60);
+ const offsetMinutes = Math.abs(timezoneOffset) % 60;
+ const offsetSign = timezoneOffset >= 0 ? "+" : "-";
+ const timezone = `UTC${offsetSign}${String(offsetHours).padStart(2, "0")}:${String(offsetMinutes).padStart(2, "0")}`;
+
+ const text = [
+ `Formatted: ${formatted}`,
+ `Date: ${now.toLocaleDateString("en-CA")}`,
+ `Time: ${now.toLocaleTimeString("en-GB", { hour12: false })}`,
+ `Timezone: ${timezone} (${Intl.DateTimeFormat().resolvedOptions().timeZone})`,
+ `Day: ${now.toLocaleDateString("en-US", { weekday: "long" })}`,
+ `Unix: ${Math.floor(now.getTime() / 1000)}`,
+ ].join("\n");
+
+ return {
+ content: [{ type: "text", text }],
+ details: {},
+ };
+ },
+ });
+
+ // ── Git rebase helper ─────────────────────────────────────
+
+ pi.on("tool_call", async (event, ctx) => {
+ if (event.toolName !== "bash") return undefined;
+
+ const command = (event.input.command as string) || "";
+
+ // Only intercept git rebase commands
+ if (!/\bgit\s+rebase\b/.test(command)) return undefined;
+
+ // Skip if already has editor config
+ if (/GIT_SEQUENCE_EDITOR|GIT_EDITOR|core\.editor/.test(command)) return undefined;
+
+ // Check if it's an interactive rebase
+ const isInteractive = /\brebase\s+(-[^\s]*i|--interactive)/.test(command);
+ const isContinue = /\brebase\s+--continue/.test(command);
+
+ if (isInteractive || isContinue) {
+ // Prepend editor env vars to prevent hanging
+ const prefix = isInteractive
+ ? 'GIT_SEQUENCE_EDITOR=":" GIT_EDITOR="true"'
+ : 'GIT_EDITOR="true"';
+
+ ctx.ui.notify(`Injected ${prefix} to prevent editor hang`, "info");
+
+ return {
+ input: {
+ ...event.input,
+ command: `${prefix} ${command}`,
+ },
+ };
+ }
+
+ return undefined;
+ });
+}
dots/pi/agent/extensions/defaults/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "defaults",
+ "version": "1.0.0",
+ "type": "module",
+ "main": "index.ts",
+ "dependencies": {
+ "@mariozechner/pi-coding-agent": "*"
+ }
+}