Commit e3762c04833e

Vincent Demeester <vincent@sbr.pm>
2026-06-29 10:59:12
refactor(pi): rename extension imports to @earendil-works scope
Renamed pi-coding-agent, pi-tui, pi-ai, and pi-agent-core imports from the frozen @mariozechner scope to the maintained @earendil-works scope across all extensions, matching the installed pi 0.80.2. Rebuilt the org-todos bundle so externals reference the new scope.
1 parent aadc9bb
.pi/extensions/deployment-guard.ts
@@ -12,8 +12,8 @@
  * - Suggests dry-build first if not already run
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
 
 // Production hosts that require extra confirmation
 const PRODUCTION_HOSTS = [
.pi/extensions/secrets-validator.ts
@@ -12,8 +12,8 @@
  * - Integrates with existing validate-git-push extension
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
 
 // Patterns that might indicate secrets
 const SECRET_PATTERNS = [
dots/pi/agent/extensions/ai-storage/index.ts
@@ -24,9 +24,9 @@
  * Compatible with Claude Code, pi, opencode, cursor, and other AI coding tools.
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { getMarkdownTheme } from "@mariozechner/pi-coding-agent";
-import { Box, Markdown, matchesKey, visibleWidth, truncateToWidth } from "@mariozechner/pi-tui";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
+import { Box, Markdown, matchesKey, visibleWidth, truncateToWidth } from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
 import { writeFile, mkdir, appendFile, readFile, readdir, unlink, stat } from "node:fs/promises";
 import { existsSync, openSync, readdirSync } from "node:fs";
dots/pi/agent/extensions/ai-storage/package.json
@@ -7,7 +7,7 @@
     "chrono-node": "^2.9.0"
   },
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*",
+    "@earendil-works/pi-coding-agent": "*",
     "bun-types": "^1.0.0"
   },
   "pi": {
dots/pi/agent/extensions/defaults/index.ts
@@ -9,11 +9,11 @@
  * Adapted from: aliou/pi-extensions defaults
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/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";
+import { createReadTool, createLsTool } from "@earendil-works/pi-coding-agent";
 
 export default function (pi: ExtensionAPI) {
 	// ── Directory-aware read ──────────────────────────────────
dots/pi/agent/extensions/defaults/package.json
@@ -4,6 +4,6 @@
   "type": "module",
   "main": "index.ts",
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*"
+    "@earendil-works/pi-coding-agent": "*"
   }
 }
dots/pi/agent/extensions/filter-output/index.ts
@@ -10,7 +10,7 @@
  * Source: adapted from michalvavra/agents filter-output.ts
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 
 // ── Sensitive data patterns ───────────────────────────────────
 
dots/pi/agent/extensions/filter-output/package.json
@@ -4,6 +4,6 @@
   "type": "module",
   "main": "index.ts",
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*"
+    "@earendil-works/pi-coding-agent": "*"
   }
 }
dots/pi/agent/extensions/git/index.ts
@@ -2,7 +2,7 @@
 // Git Extension for Pi Coding Agent
 // =============================================================================
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 import {
 	listWorktrees,
 	createWorktree,
dots/pi/agent/extensions/github/actions/checks.ts
@@ -2,7 +2,7 @@
  * CI/Checks action handlers for GitHub extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { GhDetails } from "../types";
 import { parseChecks, parseRunList, getErrorMessage, getCheckIcon, getRunStatusIcon, execGh, approvalGate, buildModifyResult, buildRejectResult } from "../utils";
 
dots/pi/agent/extensions/github/actions/issue.ts
@@ -2,7 +2,7 @@
  * Issue action handlers for GitHub extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { GhDetails } from "../types";
 import {
 	parseIssueList,
dots/pi/agent/extensions/github/actions/pr.ts
@@ -2,7 +2,7 @@
  * Pull Request action handlers for GitHub extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { GhDetails, GhLineComment } from "../types";
 import {
 	parsePRList,
dots/pi/agent/extensions/github/actions/repo.ts
@@ -2,7 +2,7 @@
  * Repository action handlers for GitHub extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { GhDetails } from "../types";
 import { parseRepo, parseReleaseList, getErrorMessage, formatRelativeDate, execGh } from "../utils";
 
dots/pi/agent/extensions/github/index.ts
@@ -14,16 +14,16 @@
  *   - Authenticated: gh auth login
  */
 
-import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
 import {
 	Text,
 	type AutocompleteItem,
 	type AutocompleteProvider,
 	type AutocompleteSuggestions,
 	fuzzyFilter,
-} from "@mariozechner/pi-tui";
+} from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
-import { StringEnum } from "@mariozechner/pi-ai";
+import { StringEnum } from "@earendil-works/pi-ai";
 
 import type { GhDetails } from "./types";
 import {
dots/pi/agent/extensions/github/package.json
@@ -6,7 +6,7 @@
     "test": "bun test github.test.ts"
   },
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*",
+    "@earendil-works/pi-coding-agent": "*",
     "bun-types": "^1.0.0"
   }
 }
dots/pi/agent/extensions/github/utils.ts
@@ -2,7 +2,7 @@
  * Utility functions for GitHub extension
  */
 
-import type { Theme, ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { Theme, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { GhDetails, GhPR, GhIssue, GhCheck, GhRun, GhReview, GhReviewComment, GhReviewSummary, GhRelease, GhRepo } from "./types";
 
 // ============================================================================
dots/pi/agent/extensions/guardrails/index.ts
@@ -13,8 +13,8 @@
  * Policy config: ~/.config/ai/path-policies.json
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
 import { existsSync } from "node:fs";
 import { readFile, writeFile } from "node:fs/promises";
 import { join, normalize } from "node:path";
dots/pi/agent/extensions/guardrails/package.json
@@ -7,7 +7,7 @@
     "extensions": ["./index.ts"]
   },
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*",
+    "@earendil-works/pi-coding-agent": "*",
     "bun-types": "^1.0.0"
   }
 }
dots/pi/agent/extensions/jira/actions.ts
@@ -2,7 +2,7 @@
  * Action handlers for Jira extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { JiraDetails } from "./types";
 import {
 	buildCommentConfirmation,
dots/pi/agent/extensions/jira/attachment-actions.ts
@@ -2,7 +2,7 @@
  * Attachment-related action handlers for Jira extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { JiraDetails } from "./types";
 import { getErrorMessage, serializedConfirm } from "./utils";
 
dots/pi/agent/extensions/jira/epic-actions.ts
@@ -2,7 +2,7 @@
  * Epic-related action handlers for Jira extension
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { JiraDetails } from "./types";
 import { getErrorMessage, serializedConfirm } from "./utils";
 
dots/pi/agent/extensions/jira/index.ts
@@ -16,16 +16,16 @@
  *   - Red Hat VPN connection
  */
 
-import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
 import {
 	Text,
 	type AutocompleteItem,
 	type AutocompleteProvider,
 	type AutocompleteSuggestions,
 	fuzzyFilter,
-} from "@mariozechner/pi-tui";
+} from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
-import { StringEnum } from "@mariozechner/pi-ai";
+import { StringEnum } from "@earendil-works/pi-ai";
 
 import type { JiraDetails } from "./types";
 import {
dots/pi/agent/extensions/jira/package.json
@@ -6,7 +6,7 @@
     "test": "bun test jira.test.ts"
   },
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*",
+    "@earendil-works/pi-coding-agent": "*",
     "bun-types": "^1.0.0"
   }
 }
dots/pi/agent/extensions/jira/utils.ts
@@ -2,7 +2,7 @@
  * Utility functions for Jira extension
  */
 
-import type { Theme, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { Theme, ExtensionContext } from "@earendil-works/pi-coding-agent";
 import type { JiraIssue } from "./types";
 
 // ============================================================================
dots/pi/agent/extensions/org-todos/dist/index.js
@@ -1,5 +1,5 @@
 // index.ts
-import { DynamicBorder } from "@mariozechner/pi-coding-agent";
+import { DynamicBorder } from "@earendil-works/pi-coding-agent";
 import { execSync } from "node:child_process";
 import { homedir } from "node:os";
 import { join } from "node:path";
@@ -483,7 +483,7 @@ class ReferenceWithTimezone {
     return date;
   }
   getSystemTimezoneAdjustmentMinute(date, overrideTimezoneOffset) {
-    if (!date || date.getTime() < 0) {
+    if (!date) {
       date = new Date;
     }
     const currentTimezoneOffset = -date.getTimezoneOffset();
@@ -986,7 +986,7 @@ function parseOrdinalNumberPattern(match) {
   num = num.replace(/(?:st|nd|rd|th)$/i, "");
   return parseInt(num);
 }
-var YEAR_PATTERN = `(?:[1-9][0-9]{0,3}\\s{0,2}(?:BE|AD|BC|BCE|CE)|[1-2][0-9]{3}|[5-9][0-9]|2[0-5])`;
+var YEAR_PATTERN = `(?:[1-9][0-9]{0,3}\\s{0,2}(?:BE|AD|BC|BCE|CE)|[1-9][0-9]{3}|[5-9][0-9]|2[0-5])`;
 function parseYear(match) {
   if (/BE/i.test(match)) {
     match = match.replace(/BE/i, "");
@@ -1813,9 +1813,10 @@ function mergeDateTimeComponent(dateComponent, timeComponent) {
   if (timeComponent.isCertain("timezoneOffset")) {
     dateTimeComponent.assign("timezoneOffset", timeComponent.get("timezoneOffset"));
   }
+  const dateHasMeaningfulMeridiem = dateComponent.get("meridiem") != null && (dateComponent.isCertain("meridiem") || Array.from(dateComponent.tags()).some((t) => t.startsWith("casualReference/")));
   if (timeComponent.isCertain("meridiem")) {
     dateTimeComponent.assign("meridiem", timeComponent.get("meridiem"));
-  } else if (timeComponent.get("meridiem") != null && dateTimeComponent.get("meridiem") == null) {
+  } else if (timeComponent.get("meridiem") != null && !dateHasMeaningfulMeridiem) {
     dateTimeComponent.imply("meridiem", timeComponent.get("meridiem"));
   }
   if (dateTimeComponent.get("meridiem") == Meridiem.PM && dateTimeComponent.get("hour") < 12) {
@@ -1977,6 +1978,63 @@ class OverlapRemovalRefiner {
   }
 }
 
+// node_modules/chrono-node/dist/esm/calculation/weekdays.js
+function createParsingComponentsAtWeekday(reference, weekday, modifier) {
+  const refDate = reference.getDateWithAdjustedTimezone();
+  const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
+  let components = new ParsingComponents(reference);
+  components = components.addDurationAsImplied({ day: daysToWeekday });
+  components.assign("weekday", weekday);
+  return components;
+}
+function getDaysToWeekday(refDate, weekday, modifier) {
+  const refWeekday = refDate.getDay();
+  switch (modifier) {
+    case "this":
+      return getDaysForwardToWeekday(refDate, weekday);
+    case "last":
+      return getBackwardDaysToWeekday(refDate, weekday);
+    case "next":
+      if (refWeekday == Weekday.SUNDAY) {
+        return weekday == Weekday.SUNDAY ? 7 : weekday;
+      }
+      if (refWeekday == Weekday.SATURDAY) {
+        if (weekday == Weekday.SATURDAY)
+          return 7;
+        if (weekday == Weekday.SUNDAY)
+          return 8;
+        return 1 + weekday;
+      }
+      if (weekday < refWeekday && weekday != Weekday.SUNDAY) {
+        return getDaysForwardToWeekday(refDate, weekday);
+      } else {
+        return getDaysForwardToWeekday(refDate, weekday) + 7;
+      }
+  }
+  return getDaysToWeekdayClosest(refDate, weekday);
+}
+function getDaysToWeekdayClosest(refDate, weekday) {
+  const backward = getBackwardDaysToWeekday(refDate, weekday);
+  const forward = getDaysForwardToWeekday(refDate, weekday);
+  return forward < -backward ? forward : backward;
+}
+function getDaysForwardToWeekday(refDate, weekday) {
+  const refWeekday = refDate.getDay();
+  let forwardCount = weekday - refWeekday;
+  if (forwardCount < 0) {
+    forwardCount += 7;
+  }
+  return forwardCount;
+}
+function getBackwardDaysToWeekday(refDate, weekday) {
+  const refWeekday = refDate.getDay();
+  let backwardCount = weekday - refWeekday;
+  if (backwardCount >= 0) {
+    backwardCount -= 7;
+  }
+  return backwardCount;
+}
+
 // node_modules/chrono-node/dist/esm/common/refiners/ForwardDateRefiner.js
 class ForwardDateRefiner {
   refine(context, results) {
@@ -2002,22 +2060,16 @@ class ForwardDateRefiner {
         }
       }
       if (result.start.isOnlyWeekdayComponent() && refDate > result.start.date()) {
-        let daysToAdd = result.start.get("weekday") - refDate.getDay();
-        if (daysToAdd <= 0) {
-          daysToAdd += 7;
-        }
-        refDate = addDuration(refDate, { day: daysToAdd });
-        implySimilarDate(result.start, refDate);
+        let daysToAdd = getDaysForwardToWeekday(refDate, result.start.get("weekday")) || 7;
+        const forwardedWeekday = addDuration(refDate, { day: daysToAdd });
+        implySimilarDate(result.start, forwardedWeekday);
         context.debug(() => {
           console.log(`${this.constructor.name} adjusted ${result} weekday (${result.start})`);
         });
-        if (result.end && result.end.isOnlyWeekdayComponent()) {
-          let daysToAdd2 = result.end.get("weekday") - refDate.getDay();
-          if (daysToAdd2 <= 0) {
-            daysToAdd2 += 7;
-          }
-          refDate = addDuration(refDate, { day: daysToAdd2 });
-          implySimilarDate(result.end, refDate);
+        if (result.end && result.start.date() > result.end.date()) {
+          let daysToAdd2 = getDaysForwardToWeekday(refDate, result.start.get("weekday")) || 7;
+          const forwardedWeekday2 = addDuration(refDate, { day: daysToAdd2 });
+          implySimilarDate(result.end, forwardedWeekday2);
           context.debug(() => {
             console.log(`${this.constructor.name} adjusted ${result} weekday (${result.end})`);
           });
@@ -2350,65 +2402,8 @@ class ENCasualTimeParser extends AbstractParserWithWordBoundaryChecking {
   }
 }
 
-// node_modules/chrono-node/dist/esm/calculation/weekdays.js
-function createParsingComponentsAtWeekday(reference, weekday, modifier) {
-  const refDate = reference.getDateWithAdjustedTimezone();
-  const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
-  let components = new ParsingComponents(reference);
-  components = components.addDurationAsImplied({ day: daysToWeekday });
-  components.assign("weekday", weekday);
-  return components;
-}
-function getDaysToWeekday(refDate, weekday, modifier) {
-  const refWeekday = refDate.getDay();
-  switch (modifier) {
-    case "this":
-      return getDaysForwardToWeekday(refDate, weekday);
-    case "last":
-      return getBackwardDaysToWeekday(refDate, weekday);
-    case "next":
-      if (refWeekday == Weekday.SUNDAY) {
-        return weekday == Weekday.SUNDAY ? 7 : weekday;
-      }
-      if (refWeekday == Weekday.SATURDAY) {
-        if (weekday == Weekday.SATURDAY)
-          return 7;
-        if (weekday == Weekday.SUNDAY)
-          return 8;
-        return 1 + weekday;
-      }
-      if (weekday < refWeekday && weekday != Weekday.SUNDAY) {
-        return getDaysForwardToWeekday(refDate, weekday);
-      } else {
-        return getDaysForwardToWeekday(refDate, weekday) + 7;
-      }
-  }
-  return getDaysToWeekdayClosest(refDate, weekday);
-}
-function getDaysToWeekdayClosest(refDate, weekday) {
-  const backward = getBackwardDaysToWeekday(refDate, weekday);
-  const forward = getDaysForwardToWeekday(refDate, weekday);
-  return forward < -backward ? forward : backward;
-}
-function getDaysForwardToWeekday(refDate, weekday) {
-  const refWeekday = refDate.getDay();
-  let forwardCount = weekday - refWeekday;
-  if (forwardCount < 0) {
-    forwardCount += 7;
-  }
-  return forwardCount;
-}
-function getBackwardDaysToWeekday(refDate, weekday) {
-  const refWeekday = refDate.getDay();
-  let backwardCount = weekday - refWeekday;
-  if (backwardCount >= 0) {
-    backwardCount -= 7;
-  }
-  return backwardCount;
-}
-
 // node_modules/chrono-node/dist/esm/locales/en/parsers/ENWeekdayParser.js
-var PATTERN11 = new RegExp("(?:(?:\\,|\\(|\\()\\s*)?" + "(?:on\\s*?)?" + "(?:(this|last|past|next)\\s*)?" + `(${matchAnyPattern(WEEKDAY_DICTIONARY)}|weekend|weekday)` + "(?:\\s*(?:\\,|\\)|\\)))?" + "(?:\\s*(this|last|past|next)\\s*week)?" + "(?=\\W|$)", "i");
+var PATTERN11 = new RegExp("(?:(?:\\,|\\(|\\()\\s*)?" + "(?:on\\s*?)?" + "(?:(this|last|past|next)\\s*)?" + `(${matchAnyPattern(WEEKDAY_DICTIONARY)}|weekend|weekday)` + "(?:\\s*(?:\\,|\\)|\\)))?" + "(?:\\s*(?:of\\s*)?(this|last|past|next)\\s*week)?" + "(?=\\W|$)", "i");
 var PREFIX_GROUP2 = 1;
 var WEEKDAY_GROUP = 2;
 var POSTFIX_GROUP = 3;
@@ -2874,7 +2869,12 @@ function parseDate(text, ref, option) {
 }
 
 // index.ts
-import { Container, SelectList, Text } from "@mariozechner/pi-tui";
+import {
+  Container,
+  SelectList,
+  Text,
+  fuzzyFilter
+} from "@earendil-works/pi-tui";
 var DEFAULT_ORG_FILE = join(homedir(), "desktop/org/todos.org");
 var INBOX_FILE = join(homedir(), "desktop/org/inbox.org");
 var DEFAULT_SECTION = "Inbox";
@@ -3041,6 +3041,12 @@ function org_todos_default(pi) {
   pi.registerTool({
     name: "org_todo",
     label: "Org TODO",
+    promptSnippet: "Manage org-mode TODOs. Actions: list, scheduled, upcoming, overdue, search, get, done, state, schedule, deadline, priority, add, append, inbox-list, inbox-count, inbox-add, refile-targets, refile",
+    promptGuidelines: [
+      "NEVER edit .org files directly — always use the org_todo tool for all TODO operations",
+      "For scheduling, use YYYY-MM-DD date format (natural language dates are NOT supported by this tool)",
+      "Use inbox-add for quick capture, then refile to the appropriate section later"
+    ],
     description: `Manage org-mode TODOs. Actions:
 - list: List active TODOs (TODO, NEXT, STRT)
 - scheduled: Get today's scheduled items
@@ -3966,15 +3972,93 @@ ${selected.display}`,
   pi.on("session_start", async (_event, ctx) => {
     updateInboxStatus(ctx);
     updateTodayStatus(ctx);
+    setupTodoAutocomplete(ctx);
     const updateInterval = setInterval(() => {
       updateInboxStatus(ctx);
       updateTodayStatus(ctx);
     }, 5 * 60 * 1000);
+    updateInterval.unref();
     pi.on("session_end", async () => {
       clearInterval(updateInterval);
     });
   });
 }
+var TODO_MAX_SUGGESTIONS = 20;
+function extractTodoToken(textBeforeCursor) {
+  const match = textBeforeCursor.match(/(?:^|[ \t])t:([^\s]*)$/);
+  return match?.[1];
+}
+function formatTodoItem(item) {
+  return {
+    value: item.heading,
+    label: item.heading,
+    description: `[${item.todo}]`
+  };
+}
+function filterTodoItems(items, query) {
+  if (!query.trim()) {
+    return items.slice(0, TODO_MAX_SUGGESTIONS).map(formatTodoItem);
+  }
+  return fuzzyFilter(items, query, (item) => `${item.todo} ${item.heading}`).slice(0, TODO_MAX_SUGGESTIONS).map(formatTodoItem);
+}
+function createTodoAutocompleteProvider(current, getItems) {
+  return {
+    async getSuggestions(lines, cursorLine, cursorCol, options) {
+      const currentLine = lines[cursorLine] ?? "";
+      const textBeforeCursor = currentLine.slice(0, cursorCol);
+      const query = extractTodoToken(textBeforeCursor);
+      if (query === undefined) {
+        return current.getSuggestions(lines, cursorLine, cursorCol, options);
+      }
+      const items = await getItems();
+      if (options.signal.aborted || !items || items.length === 0) {
+        return current.getSuggestions(lines, cursorLine, cursorCol, options);
+      }
+      const suggestions = filterTodoItems(items, query);
+      if (suggestions.length === 0) {
+        return current.getSuggestions(lines, cursorLine, cursorCol, options);
+      }
+      return { items: suggestions, prefix: `t:${query}` };
+    },
+    applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
+      if (prefix.startsWith("t:")) {
+        const currentLine = lines[cursorLine] || "";
+        const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
+        const afterCursor = currentLine.slice(cursorCol);
+        const value = item.value.includes(" ") ? `"${item.value}"` : item.value;
+        const newLine = beforePrefix + value + " " + afterCursor;
+        const newLines = [...lines];
+        newLines[cursorLine] = newLine;
+        return {
+          lines: newLines,
+          cursorLine,
+          cursorCol: beforePrefix.length + value.length + 1
+        };
+      }
+      return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
+    },
+    shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
+      return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
+    }
+  };
+}
+function setupTodoAutocomplete(ctx) {
+  let itemsPromise;
+  const getItems = async () => {
+    itemsPromise ||= (async () => {
+      const result = execEmacs("(pi/org-todo-list)");
+      if (!result.success || !Array.isArray(result.data))
+        return;
+      return result.data.map((item) => ({
+        heading: item.heading,
+        todo: item.todo || "TODO"
+      }));
+    })();
+    return itemsPromise;
+  };
+  getItems();
+  ctx.ui.addAutocompleteProvider((current) => createTodoAutocompleteProvider(current, getItems));
+}
 export {
   org_todos_default as default
 };
dots/pi/agent/extensions/org-todos/index.ts
@@ -30,8 +30,8 @@
  *   - org-ql and pi-org-todos.el loaded
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
-import { DynamicBorder } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
+import { DynamicBorder } from "@earendil-works/pi-coding-agent";
 import { execSync } from "node:child_process";
 import { homedir } from "node:os";
 import { join } from "node:path";
@@ -45,7 +45,7 @@ import {
 	type AutocompleteProvider,
 	type AutocompleteSuggestions,
 	fuzzyFilter,
-} from "@mariozechner/pi-tui";
+} from "@earendil-works/pi-tui";
 
 // Configuration
 const DEFAULT_ORG_FILE = join(homedir(), "desktop/org/todos.org");
dots/pi/agent/extensions/org-todos/Makefile
@@ -121,7 +121,7 @@ test-daemon-check:
 # Build TypeScript
 build:
 	@echo -e "$(YELLOW)Building TypeScript...$(NC)"
-	@bun build index.ts --target=node --outdir dist --external @mariozechner/pi-coding-agent --external @mariozechner/pi-tui
+	@bun build index.ts --target=node --outdir dist --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui
 	@echo -e "$(GREEN)✓ Build complete: dist/index.js$(NC)"
 
 # Clean build artifacts
dots/pi/agent/extensions/org-todos/package.json
@@ -9,7 +9,7 @@
     "chrono-node": "^2.7.0"
   },
   "devDependencies": {
-    "@mariozechner/pi-coding-agent": "*",
+    "@earendil-works/pi-coding-agent": "*",
     "bun-types": "^1.0.0"
   }
 }
dots/pi/agent/extensions/search/index.ts
@@ -19,8 +19,8 @@
  *   SEARXNG_URL  - SearXNG instance URL (default: https://search.sbr.pm)
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { Text } from "@mariozechner/pi-tui";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { Text } from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
 import {
   BraveAPIBackend,
dots/pi/agent/extensions/shell-completions/bash.ts
@@ -7,7 +7,7 @@
  * Philosophy: Only provide completions if the user has bash-completion available.
  */
 
-import type { AutocompleteItem } from "@mariozechner/pi-tui";
+import type { AutocompleteItem } from "@earendil-works/pi-tui";
 import { spawnSync } from "node:child_process";
 import * as fs from "node:fs";
 import * as path from "node:path";
dots/pi/agent/extensions/shell-completions/fish.ts
@@ -8,7 +8,7 @@
  * returns null for "user hasn't configured completions".
  */
 
-import type { AutocompleteItem } from "@mariozechner/pi-tui";
+import type { AutocompleteItem } from "@earendil-works/pi-tui";
 import { spawnSync } from "node:child_process";
 import type { CompletionResult, ShellCompletionProvider } from "./types.js";
 
dots/pi/agent/extensions/shell-completions/index.ts
@@ -17,8 +17,8 @@
  * This means completions respect the user's shell setup, aliases, and customizations.
  */
 
-import { CustomEditor, type ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import type { AutocompleteItem, AutocompleteProvider } from "@mariozechner/pi-tui";
+import { CustomEditor, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import type { AutocompleteItem, AutocompleteProvider } from "@earendil-works/pi-tui";
 import * as fs from "node:fs";
 import * as path from "node:path";
 
dots/pi/agent/extensions/shell-completions/package.json
@@ -14,7 +14,7 @@
     "extensions": ["./index.ts"]
   },
   "peerDependencies": {
-    "@mariozechner/pi-coding-agent": "*",
-    "@mariozechner/pi-tui": "*"
+    "@earendil-works/pi-coding-agent": "*",
+    "@earendil-works/pi-tui": "*"
   }
 }
dots/pi/agent/extensions/shell-completions/types.ts
@@ -2,7 +2,7 @@
  * Shared types for shell completions extension.
  */
 
-import type { AutocompleteItem } from "@mariozechner/pi-tui";
+import type { AutocompleteItem } from "@earendil-works/pi-tui";
 
 export type ShellType = "fish" | "zsh" | "bash";
 
dots/pi/agent/extensions/shell-completions/zsh.ts
@@ -18,7 +18,7 @@ import * as fs from "node:fs";
 import * as path from "node:path";
 import { fileURLToPath } from "node:url";
 import type { CompletionResult } from "./types.js";
-import type { AutocompleteItem } from "@mariozechner/pi-tui";
+import type { AutocompleteItem } from "@earendil-works/pi-tui";
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 const CAPTURE_SCRIPT = path.join(__dirname, "scripts", "zsh-capture.zsh");
dots/pi/agent/extensions/subagent/agents.ts
@@ -5,7 +5,7 @@
 import * as fs from "node:fs";
 import * as os from "node:os";
 import * as path from "node:path";
-import { parseFrontmatter } from "@mariozechner/pi-coding-agent";
+import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
 
 export type AgentScope = "user" | "project" | "both";
 
dots/pi/agent/extensions/subagent/index.ts
@@ -16,11 +16,11 @@ import { spawn } from "node:child_process";
 import * as fs from "node:fs";
 import * as os from "node:os";
 import * as path from "node:path";
-import type { AgentToolResult } from "@mariozechner/pi-agent-core";
-import type { Message } from "@mariozechner/pi-ai";
-import { StringEnum } from "@mariozechner/pi-ai";
-import { type ExtensionAPI, getMarkdownTheme } from "@mariozechner/pi-coding-agent";
-import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
+import type { AgentToolResult } from "@earendil-works/pi-agent-core";
+import type { Message } from "@earendil-works/pi-ai";
+import { StringEnum } from "@earendil-works/pi-ai";
+import { type ExtensionAPI, getMarkdownTheme } from "@earendil-works/pi-coding-agent";
+import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
 import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.js";
 
dots/pi/agent/extensions/threads/index.ts
@@ -9,15 +9,15 @@ import * as fs from "node:fs";
 import * as os from "node:os";
 import * as path from "node:path";
 import { Type } from "@sinclair/typebox";
-import { StringEnum } from "@mariozechner/pi-ai";
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import { StringEnum } from "@earendil-works/pi-ai";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 import {
 	parseSessionEntries,
 	type FileEntry,
 	type SessionEntry,
 	type SessionHeader,
-} from "@mariozechner/pi-coding-agent";
-import { Text, Container, Spacer } from "@mariozechner/pi-tui";
+} from "@earendil-works/pi-coding-agent";
+import { Text, Container, Spacer } from "@earendil-works/pi-tui";
 
 // ============================================================================
 // Session Parsing Utilities
dots/pi/agent/extensions/threads/package.json
@@ -14,9 +14,9 @@
     "extensions": ["./index.ts"]
   },
   "peerDependencies": {
-    "@mariozechner/pi-ai": "*",
-    "@mariozechner/pi-coding-agent": "*",
-    "@mariozechner/pi-tui": "*",
+    "@earendil-works/pi-ai": "*",
+    "@earendil-works/pi-coding-agent": "*",
+    "@earendil-works/pi-tui": "*",
     "@sinclair/typebox": "*"
   }
 }
dots/pi/agent/extensions/ask-user.ts
@@ -11,9 +11,9 @@
  *   ask_user({ question: "Which features?", suggestions: ["auth", "logging", "caching"], multiSelect: true })
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { DynamicBorder, getSettingsListTheme } from "@mariozechner/pi-coding-agent";
-import { Container, type SettingItem, SettingsList, Text } from "@mariozechner/pi-tui";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { DynamicBorder, getSettingsListTheme } from "@earendil-works/pi-coding-agent";
+import { Container, type SettingItem, SettingsList, Text } from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
 
 /**
dots/pi/agent/extensions/cost-tracker.ts
@@ -5,7 +5,7 @@
  * similar to GitHub Copilot's premium request multiplier display.
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 
 // Premium request multipliers (cost relative to Sonnet baseline)
 const MULTIPLIERS: Record<string, number> = {
dots/pi/agent/extensions/custom-footer.ts
@@ -12,8 +12,8 @@
  * Format: ️ kyushu  ~/s/home  main  sonnet-4  76.2%/200k  16:10
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { truncateToWidth } from "@mariozechner/pi-tui";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { truncateToWidth } from "@earendil-works/pi-tui";
 import { hostname } from "node:os";
 import { execSync } from "node:child_process";
 import path from "node:path";
dots/pi/agent/extensions/custom-header.ts
@@ -8,8 +8,8 @@
  * - /resources command to show loaded extensions/skills/prompts inline
  */
 
-import type { ExtensionAPI, Theme } from "@mariozechner/pi-coding-agent";
-import { VERSION } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
+import { VERSION } from "@earendil-works/pi-coding-agent";
 
 // Pi symbol ASCII art (using Braille patterns) with green-to-blue gradient
 function getPiArt(theme: Theme): string[] {
dots/pi/agent/extensions/debug-requests.ts
@@ -9,7 +9,7 @@
  * Logs to: /tmp/pi-requests.log (truncated per session)
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 import { appendFileSync, writeFileSync } from "node:fs";
 
 const LOG_FILE = "/tmp/pi-requests.log";
dots/pi/agent/extensions/interactive-shell.ts
@@ -21,7 +21,7 @@
  */
 
 import { spawnSync } from "node:child_process";
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 
 // Default interactive commands - editors, pagers, git ops, TUIs
 const DEFAULT_INTERACTIVE_COMMANDS = [
dots/pi/agent/extensions/kitty-image.ts
@@ -11,9 +11,9 @@
  * - /show-image command: quick display from the command line
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { convertToPng as convertToPngNative } from "@mariozechner/pi-coding-agent";
-import { Text } from "@mariozechner/pi-tui";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { convertToPng as convertToPngNative } from "@earendil-works/pi-coding-agent";
+import { Text } from "@earendil-works/pi-tui";
 import { Type } from "@sinclair/typebox";
 import { readFileSync, existsSync, statSync } from "node:fs";
 import { execSync, execFileSync } from "node:child_process";
dots/pi/agent/extensions/message-queue.ts
@@ -17,9 +17,9 @@
  *   - Escape to close without sending
  */
 
-import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
-import { DynamicBorder } from "@mariozechner/pi-coding-agent";
-import { matchesKey, Key, truncateToWidth } from "@mariozechner/pi-tui";
+import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
+import { DynamicBorder } from "@earendil-works/pi-coding-agent";
+import { matchesKey, Key, truncateToWidth } from "@earendil-works/pi-tui";
 
 interface QueuedMessage {
 	text: string;
dots/pi/agent/extensions/orc-mode.ts
@@ -16,7 +16,7 @@
  * 2. Or start with --orc flag
  */
 
-import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
 
 // Tools available in orchestrator mode - subagent for delegation, subagent_status for async monitoring
 const ORC_MODE_TOOLS = ["subagent", "subagent_status"];
dots/pi/agent/extensions/prompt-editor.ts
@@ -1,6 +1,6 @@
-import type { ExtensionAPI, ExtensionContext, ModelSelectEvent, ThinkingLevel } from "@mariozechner/pi-coding-agent";
-import { CustomEditor, DynamicBorder, ModelSelectorComponent, SettingsManager } from "@mariozechner/pi-coding-agent";
-import { Container, type SelectItem, SelectList, Text, matchesKey, Key } from "@mariozechner/pi-tui";
+import type { ExtensionAPI, ExtensionContext, ModelSelectEvent, ThinkingLevel } from "@earendil-works/pi-coding-agent";
+import { CustomEditor, DynamicBorder, ModelSelectorComponent, SettingsManager } from "@earendil-works/pi-coding-agent";
+import { Container, type SelectItem, SelectList, Text, matchesKey, Key } from "@earendil-works/pi-tui";
 import path from "node:path";
 import os from "node:os";
 import fs from "node:fs/promises";
dots/pi/agent/extensions/review.ts
@@ -28,9 +28,9 @@
  * Note: PR review requires a clean working tree (no uncommitted changes to tracked files).
  */
 
-import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
-import { DynamicBorder, BorderedLoader } from "@mariozechner/pi-coding-agent";
-import { Container, type SelectItem, SelectList, Text, Key } from "@mariozechner/pi-tui";
+import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
+import { DynamicBorder, BorderedLoader } from "@earendil-works/pi-coding-agent";
+import { Container, type SelectItem, SelectList, Text, Key } from "@earendil-works/pi-tui";
 import path from "node:path";
 import { promises as fs, readdirSync, readFileSync } from "node:fs";
 import { execSync } from "node:child_process";
dots/pi/agent/extensions/subagent-commands.ts
@@ -8,7 +8,7 @@
  * - /review-code <query> - Review workflow: scout → reviewer
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 
 export default function (pi: ExtensionAPI) {
 	// /scout - Quick reconnaissance
dots/pi/agent/extensions/terminal-status.ts
@@ -22,7 +22,7 @@
  * Migrated from: notify.ts + Go-based claude-hooks-update-terminal-title
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 import { execSync } from "node:child_process";
 import { hostname } from "node:os";
 import path from "node:path";
dots/pi/agent/extensions/tool-filter.ts
@@ -14,7 +14,7 @@
  *   PI_ALL_TOOLS=1     — Disable filtering entirely
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 import { existsSync } from "fs";
 import { join } from "path";
 
dots/pi/agent/extensions/usage-bar.ts
@@ -8,8 +8,8 @@
  * - Reset countdowns
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { visibleWidth } from "@mariozechner/pi-tui";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
+import { visibleWidth } from "@earendil-works/pi-tui";
 import * as fs from "node:fs";
 import * as path from "node:path";
 import * as os from "node:os";
dots/pi/agent/extensions/validate-git-push.ts
@@ -14,7 +14,7 @@
  * 3. Use specific file paths for git add
  */
 
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
 
 /**
  * Check if a git push command uses explicit refspec (branch:branch)