pi-posthorse 0.5.0 → 0.7.0

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.
Files changed (4) hide show
  1. package/README.md +41 -47
  2. package/index.ts +726 -317
  3. package/package.json +12 -9
  4. package/ui.ts +17 -7
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "pi-posthorse",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
- "description": "Posthorse: fresh context, same journey. Native no-summary context windows for the fitchmultz/pi fork, with sparse budget reminders, handoffs, notes, and history.",
5
+ "description": "Posthorse: fresh context, same journey. No-summary context rollover for Pi, with sparse budget reminders, handoffs, notes, and history.",
6
6
  "keywords": ["pi-package", "pi-extension", "context", "posthorse", "rollover"],
7
7
  "repository": {
8
8
  "type": "git",
@@ -11,16 +11,17 @@
11
11
  "license": "MIT",
12
12
  "files": ["index.ts", "ui.ts"],
13
13
  "engines": {
14
- "node": ">=22.19.0"
14
+ "node": ">=24.12.0"
15
15
  },
16
+ "packageManager": "npm@12.1.0",
16
17
  "pi": {
17
18
  "extensions": ["./index.ts"],
18
19
  "image": "https://raw.githubusercontent.com/fitchmultz/pi-posthorse/main/diagram.png"
19
20
  },
20
21
  "scripts": {
21
- "check": "tsc --noEmit",
22
+ "check": "tsc",
22
23
  "check:compat": "npm run check && npm test && npm run test:native",
23
- "test:native": "node --import ./test/pi-loader.ts --test test/native.test.mjs",
24
+ "test:native": "node --import ./test/pi-loader.ts --test test/host.test.mjs",
24
25
  "test": "node --import ./test/pi-loader.ts --test test/*.test.ts",
25
26
  "prepublishOnly": "npm test && npm run check"
26
27
  },
@@ -41,9 +42,11 @@
41
42
  }
42
43
  },
43
44
  "devDependencies": {
44
- "@earendil-works/pi-coding-agent": "0.87.0",
45
- "@earendil-works/pi-tui": "0.87.0",
46
- "typebox": "1.3.7",
47
- "typescript": "^5.5.0"
45
+ "@earendil-works/pi-ai": "0.87.1",
46
+ "@earendil-works/pi-coding-agent": "0.87.1",
47
+ "@earendil-works/pi-tui": "0.87.1",
48
+ "@types/node": "24.19.0",
49
+ "typebox": "1.3.34",
50
+ "typescript": "7.0.2"
48
51
  }
49
52
  }
package/ui.ts CHANGED
@@ -7,8 +7,8 @@ export type PosthorseDisplay =
7
7
  | { kind: "notes-list" | "notes-search"; count: number; page?: { offset: number; end: number; total: number } }
8
8
  | { kind: "note-read"; offset: number; end: number; total: number }
9
9
  | { kind: "note-write" | "note-append" | "new-context" }
10
- | { kind: "history-search"; entries: Array<{ headerLength: number; length: number }>; footerLength?: number }
11
- | { kind: "history-read"; headerLength: number; offset: number; end: number; total: number };
10
+ | { kind: "history-search"; entries: Array<{ headerLength: number; length: number }>; footerLength?: number; more?: boolean; skipped?: number }
11
+ | { kind: "history-read"; headerLength: number; offset: number; end: number; total: number; imageOffset?: number; imageEnd?: number; imageTotal?: number };
12
12
 
13
13
  type ToolName = "notes" | "history" | "get_context_remaining" | "new_context";
14
14
  const titles: Record<ToolName, string> = { notes: "Notes", history: "History", get_context_remaining: "Context budget", new_context: "New context" };
@@ -62,7 +62,7 @@ export function toolCards(name: ToolName): Pick<ToolDefinition, "renderCall" | "
62
62
  const args = argsOf(rawArgs);
63
63
  const action = name === "notes" || name === "history" ? ` · ${argument(args.op, "op")}` : name === "new_context" ? " · request" : "";
64
64
  const status = context.isError ? " · error" : context.isPartial ? context.executionStarted ? " · running" : " · preparing" : "";
65
- const target = args.op === "search" ? ` · “${argument(args.query, "query")}”` : name === "notes" && args.op !== "list" ? ` · ${argument(args.path, "path")}` : name === "history" && args.op === "read" ? ` · ${argument(args.id, "id")}` : "";
65
+ const target = args.op === "search" ? ` · “${argument(args.query, "query")}”` : name === "notes" && (args.op !== "list" || args.path !== undefined) ? ` · ${argument(args.path, "path")}` : name === "history" && args.op === "read" ? ` · ${argument(args.id, "id")}` : "";
66
66
  const label = `${context.isPartial ? "…" : context.expanded ? "▾" : "▸"} ${titles[name]}${action}${status}${target}`;
67
67
  return {
68
68
  invalidate() {},
@@ -72,6 +72,7 @@ export function toolCards(name: ToolName): Pick<ToolDefinition, "renderCall" | "
72
72
  const details = [
73
73
  name === "history" && args.op === "search" ? `Scope: ${args.all === true ? "all project sessions" : "current branch"}` : "",
74
74
  args.offset !== undefined ? `Offset: ${typeof args.offset === "number" ? n(args.offset) : "[invalid offset]"}` : "",
75
+ args.imageOffset !== undefined ? `Image offset: ${typeof args.imageOffset === "number" ? n(args.imageOffset) : "[invalid image offset]"}` : "",
75
76
  args.limit !== undefined ? `Limit: ${typeof args.limit === "number" ? n(args.limit) : "[invalid limit]"}` : "",
76
77
  ].filter(Boolean).join(" · ");
77
78
  return [...new Text(heading, 0, 0).render(width), ...textBlock(details, theme, "muted").render(width)];
@@ -102,9 +103,18 @@ export function toolCards(name: ToolName): Pick<ToolDefinition, "renderCall" | "
102
103
  case "note-read": summary = pageSummary(display); break;
103
104
  case "note-write": summary = args.content === "" ? "Cleared note" : "Saved note"; break;
104
105
  case "note-append": summary = "Appended to note"; break;
105
- case "history-search": if (Array.isArray(display.entries) && (!display.entries.length || sections)) summary = `${display.entries.length ? `${n(display.entries.length)} matches` : "No matches"} · ${args.all === true ? "all sessions" : "current branch"}${display.footerLength ? "\nMore results; continue with cursor" : ""}`; break;
106
- case "history-read": summary = pageSummary(display); break;
107
- case "new-context": summary = "Requested for after the whole tool batch succeeds."; break;
106
+ case "history-search": if (Array.isArray(display.entries) && (!display.entries.length || sections)) summary = `${display.entries.length ? `${n(display.entries.length)} matches` : "No matches"} · ${args.all === true ? "all sessions" : "current branch"}${display.skipped ? ` · ${n(display.skipped)} in context skipped` : ""}${(display.more ?? display.footerLength) ? "\nMore results; continue with cursor" : ""}`; break;
107
+ case "history-read":
108
+ summary = pageSummary(display);
109
+ if (display.imageTotal && display.imageEnd !== undefined) {
110
+ summary = summary.split("\n")[0];
111
+ summary += `\nImages ${n(display.imageOffset ?? 0)}–${n(display.imageEnd)} of ${n(display.imageTotal)}`;
112
+ if (display.end < display.total || display.imageEnd < display.imageTotal) {
113
+ summary = `Next offset ${n(display.end)}\nimageOffset ${n(display.imageEnd)}\n${summary}`;
114
+ }
115
+ }
116
+ break;
117
+ case "new-context": summary = "Requested after foreground tools succeed.\nBackground work continues."; break;
108
118
  }
109
119
  }
110
120
  if (!context.isError && !isPartial && (display?.kind === "notes-list" || display?.kind === "notes-search") && display.page && (display.page.offset || display.page.end < display.page.total)) summary += `\n${pageSummary(display.page)}`;
@@ -180,5 +190,5 @@ export function registerPosthorseMessages(pi: ExtensionAPI): void {
180
190
  return { handled: true };
181
191
  });
182
192
  };
183
- for (const type of ["context-window", "posthorse-reminder", "headroom-reminder"]) pi.registerMessageRenderer(type, renderer);
193
+ for (const type of ["context-window", "posthorse-reminder"]) pi.registerMessageRenderer(type, renderer);
184
194
  }