@pi-unipi/core 2.6.1 → 2.9.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.
package/constants.ts CHANGED
@@ -38,7 +38,6 @@ export const MODULES = {
38
38
  FOOTER: "@pi-unipi/footer",
39
39
  UPDATER: "@pi-unipi/updater",
40
40
  INPUT_SHORTCUTS: "@pi-unipi/input-shortcuts",
41
- COCOINDEX: "@pi-unipi/cocoindex",
42
41
  IMAGE: "@pi-unipi/image",
43
42
  } as const;
44
43
 
@@ -134,13 +133,6 @@ export const MEMORY_DEFAULTS = {
134
133
  } as const;
135
134
 
136
135
  /** Memory types */
137
- export const MEMORY_TYPES = {
138
- PREFERENCE: "preference",
139
- DECISION: "decision",
140
- PATTERN: "pattern",
141
- SUMMARY: "summary",
142
- } as const;
143
-
144
136
  /** Default ralph loop settings */
145
137
  export const RALPH_DEFAULTS = {
146
138
  MAX_ITERATIONS: 50,
@@ -370,23 +362,3 @@ export const COMPACTOR_DEFAULTS = {
370
362
  CACHE_TTL_HOURS: 24,
371
363
  } as const;
372
364
 
373
- /** CocoIndex minimum supported CLI version. */
374
- export const COCOINDEX_MIN_VERSION = "1.0" as const;
375
-
376
- /** CocoIndex package spec installed by the interactive installer. */
377
- export const COCOINDEX_PACKAGE_SPEC = `cocoindex[lancedb]>=${COCOINDEX_MIN_VERSION}` as const;
378
-
379
- /** CocoIndex tool names */
380
- export const COCOINDEX_TOOLS = {
381
- SEARCH: "cocoindex_search",
382
- STATUS: "cocoindex_status",
383
- } as const;
384
-
385
- /** CocoIndex command names */
386
- export const COCOINDEX_COMMANDS = {
387
- UPDATE: "cocoindex-update",
388
- STATUS: "cocoindex-status",
389
- INIT: "cocoindex-init",
390
- SETTINGS: "cocoindex-settings",
391
- SEARCH: "cocoindex-search",
392
- } as const;
package/events.ts CHANGED
@@ -9,8 +9,6 @@
9
9
  export const UNIPI_EVENTS = {
10
10
  /** Module loaded and ready */
11
11
  MODULE_READY: "unipi:module:ready",
12
- /** @deprecated No built-in emitter; retained for external compatibility. */
13
- MODULE_GONE: "unipi:module:gone",
14
12
 
15
13
  /** Workflow command started */
16
14
  WORKFLOW_START: "unipi:workflow:start",
@@ -24,10 +22,6 @@ export const UNIPI_EVENTS = {
24
22
  /** Ralph loop iteration completed */
25
23
  RALPH_ITERATION_DONE: "unipi:ralph:iteration:done",
26
24
 
27
- /** @deprecated No built-in responders; use the info registry and /unipi:info. */
28
- MODULE_STATUS_REQUEST: "unipi:module:status:request",
29
- /** @deprecated No built-in aggregator; use the info registry and /unipi:info. */
30
- MODULE_STATUS_RESPONSE: "unipi:module:status:response",
31
25
 
32
26
  /** Info screen group registered */
33
27
  INFO_GROUP_REGISTERED: "unipi:info:group:registered",
@@ -38,8 +32,6 @@ export const UNIPI_EVENTS = {
38
32
  MEMORY_STORED: "unipi:memory:stored",
39
33
  /** Memory deleted */
40
34
  MEMORY_DELETED: "unipi:memory:deleted",
41
- /** @deprecated No built-in emitter; retained for external compatibility. */
42
- MEMORY_SEARCHED: "unipi:memory:searched",
43
35
  /** Memory consolidation completed */
44
36
  MEMORY_CONSOLIDATED: "unipi:memory:consolidated",
45
37
 
@@ -60,18 +52,12 @@ export const UNIPI_EVENTS = {
60
52
  COMPACTOR_COMPACTED: "unipi:compactor:compacted",
61
53
  /** @deprecated Footer reads live Pi session data; retained for compatibility. */
62
54
  COMPACTOR_STATS_UPDATED: "unipi:compactor:stats:updated",
63
- /** @deprecated No built-in emitter; retained for external compatibility. */
64
- UTILITY_CLEANUP_START: "unipi:utility:cleanup:start",
65
55
  /** Utility module cleanup completed */
66
56
  UTILITY_CLEANUP_DONE: "unipi:utility:cleanup:done",
67
57
  /** Utility diagnostics started */
68
58
  UTILITY_DIAGNOSTICS_START: "unipi:utility:diagnostics:start",
69
59
  /** Utility diagnostics completed */
70
60
  UTILITY_DIAGNOSTICS_DONE: "unipi:utility:diagnostics:done",
71
- /** @deprecated No built-in emitter; retained for external compatibility. */
72
- UTILITY_CACHE_INVALIDATED: "unipi:utility:cache:invalidated",
73
- /** @deprecated No built-in emitter; retained for external compatibility. */
74
- UTILITY_LIFECYCLE_STATE: "unipi:utility:lifecycle:state",
75
61
 
76
62
  /** Notification sent */
77
63
  NOTIFICATION_SENT: "unipi:notify:sent",
@@ -91,12 +77,6 @@ export const UNIPI_EVENTS = {
91
77
  /** Update error */
92
78
  UPDATE_ERROR: "unipi:update:error",
93
79
 
94
- /** @deprecated Reserved external contract; no built-in emitter. */
95
- COCOINDEX_UPDATE_STARTED: "unipi:cocoindex:update:started",
96
- /** @deprecated Reserved external contract; no built-in emitter. */
97
- COCOINDEX_UPDATE_COMPLETED: "unipi:cocoindex:update:completed",
98
- /** @deprecated Reserved external contract; no built-in emitter. */
99
- COCOINDEX_SEARCH_PERFORMED: "unipi:cocoindex:search:performed",
100
80
  } as const;
101
81
 
102
82
  /** Payload for MODULE_READY / MODULE_GONE */
@@ -151,21 +131,7 @@ export interface UnipiRalphIterationEvent {
151
131
  nextIteration: number;
152
132
  }
153
133
 
154
- /** @deprecated No built-in responders; retained for compatibility. */
155
- export interface UnipiStatusRequestEvent {
156
- /** Request ID for correlation */
157
- requestId: string;
158
- }
159
134
 
160
- /** @deprecated No built-in aggregator; retained for compatibility. */
161
- export interface UnipiStatusResponseEvent {
162
- /** Request ID this responds to */
163
- requestId: string;
164
- /** Module name */
165
- name: string;
166
- /** Module status data */
167
- status: Record<string, unknown>;
168
- }
169
135
 
170
136
  /** Payload for MEMORY_STORED */
171
137
  export interface UnipiMemoryStoredEvent {
@@ -191,15 +157,6 @@ export interface UnipiMemoryDeletedEvent {
191
157
  project: string;
192
158
  }
193
159
 
194
- /** Payload for MEMORY_SEARCHED */
195
- export interface UnipiMemorySearchedEvent {
196
- /** Search query */
197
- query: string;
198
- /** Number of results */
199
- resultCount: number;
200
- /** Project scope */
201
- project: string;
202
- }
203
160
 
204
161
  /** Payload for MEMORY_CONSOLIDATED */
205
162
  export interface UnipiMemoryConsolidatedEvent {
@@ -307,15 +264,6 @@ export interface UnipiUtilityDiagnosticsEvent {
307
264
  report?: unknown;
308
265
  }
309
266
 
310
- /** Payload for UTILITY_LIFECYCLE_STATE */
311
- export interface UnipiUtilityLifecycleEvent {
312
- /** New lifecycle state */
313
- state: "running" | "shutting_down" | "orphaned" | "error";
314
- /** Previous state */
315
- previousState?: string;
316
- /** Reason for state change */
317
- reason?: string;
318
- }
319
267
 
320
268
  /** Payload for BADGE_GENERATE_REQUEST */
321
269
  export interface UnipiBadgeGenerateRequestEvent {
@@ -375,31 +323,8 @@ export interface UnipiUpdateErrorEvent {
375
323
  phase: "check" | "install";
376
324
  }
377
325
 
378
- /** Payload for COCOINDEX_UPDATE_STARTED */
379
- export interface UnipiCocoindexUpdateStartedEvent {
380
- /** Project directory being indexed */
381
- projectDir: string;
382
- }
383
326
 
384
- /** Payload for COCOINDEX_UPDATE_COMPLETED */
385
- export interface UnipiCocoindexUpdateCompletedEvent {
386
- /** Whether the update succeeded */
387
- success: boolean;
388
- /** Number of chunks processed */
389
- chunksProcessed: number;
390
- /** Duration in ms */
391
- durationMs: number;
392
- /** Error message if failed */
393
- error?: string;
394
- }
395
327
 
396
- /** Payload for COCOINDEX_SEARCH_PERFORMED */
397
- export interface UnipiCocoindexSearchPerformedEvent {
398
- /** Search query */
399
- query: string;
400
- /** Number of results */
401
- resultCount: number;
402
- }
403
328
 
404
329
  /** Payload for NOTIFICATION_SENT */
405
330
  export interface UnipiNotificationSentEvent {
@@ -421,11 +346,8 @@ export type UnipiEventPayload =
421
346
  | UnipiWorkflowEvent
422
347
  | UnipiRalphLoopEvent
423
348
  | UnipiRalphIterationEvent
424
- | UnipiStatusRequestEvent
425
- | UnipiStatusResponseEvent
426
349
  | UnipiMemoryStoredEvent
427
350
  | UnipiMemoryDeletedEvent
428
- | UnipiMemorySearchedEvent
429
351
  | UnipiMemoryConsolidatedEvent
430
352
  | UnipiInfoGroupEvent
431
353
  | UnipiInfoDataEvent
@@ -436,14 +358,10 @@ export type UnipiEventPayload =
436
358
  | UnipiCompactorStatsEvent
437
359
  | UnipiUtilityCleanupEvent
438
360
  | UnipiUtilityDiagnosticsEvent
439
- | UnipiUtilityLifecycleEvent
440
361
  | UnipiNotificationSentEvent
441
362
  | UnipiBadgeGenerateRequestEvent
442
363
  | UnipiAskUserPromptEvent
443
364
  | UnipiUpdateCheckEvent
444
365
  | UnipiUpdateAvailableEvent
445
366
  | UnipiUpdateAppliedEvent
446
- | UnipiUpdateErrorEvent
447
- | UnipiCocoindexUpdateStartedEvent
448
- | UnipiCocoindexUpdateCompletedEvent
449
- | UnipiCocoindexSearchPerformedEvent;
367
+ | UnipiUpdateErrorEvent;
package/index.ts CHANGED
@@ -10,4 +10,5 @@ export * from "./sandbox.js";
10
10
  export * from "./utils.js";
11
11
  export * from "./model-cache.js";
12
12
  export * from "./tui-width.js";
13
+ export * from "./tui-overlay.js";
13
14
  export * from "./bounded-output.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/core",
3
- "version": "2.6.1",
3
+ "version": "2.9.0",
4
4
  "description": "Shared utilities, event types, and constants for Unipi extension suite",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,11 +24,13 @@
24
24
  "access": "public"
25
25
  },
26
26
  "peerDependencies": {
27
- "@earendil-works/pi-coding-agent": "^0.80.0",
27
+ "@earendil-works/pi-coding-agent": "^0.84.0",
28
+ "@earendil-works/pi-tui": "^0.84.0",
28
29
  "typebox": "^1.1.38"
29
30
  },
30
31
  "devDependencies": {
31
- "@earendil-works/pi-coding-agent": "^0.80.0",
32
+ "@earendil-works/pi-coding-agent": "^0.84.0",
33
+ "@earendil-works/pi-tui": "^0.84.0",
32
34
  "typebox": "^1.1.38",
33
35
  "@types/node": "^22.0.0"
34
36
  },
package/tui-overlay.ts ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @pi-unipi/core — Shared TUI overlay helpers
3
+ *
4
+ * Eliminates the frameLine/borderLine/ruleLine/fg/bold duplication across
5
+ * 9+ overlay files. Each overlay creates an OverlayTheme instance and
6
+ * delegates box-drawing and theming to it.
7
+ *
8
+ * The key-handling bug (raw \x1b comparisons vs matchesKey) would have been
9
+ * caught at one site instead of 10 if these helpers had existed from the start.
10
+ */
11
+ import type { Theme } from "@earendil-works/pi-coding-agent";
12
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
13
+ import { safeRepeat } from "./tui-width.js";
14
+
15
+ /** ANSI codes used when no Theme is available (terminal default). */
16
+ const FALLBACK_COLORS: Record<string, string> = {
17
+ accent: "\x1b[36m",
18
+ success: "\x1b[32m",
19
+ warning: "\x1b[33m",
20
+ error: "\x1b[31m",
21
+ dim: "\x1b[2m",
22
+ borderMuted: "\x1b[90m",
23
+ };
24
+
25
+ /**
26
+ * Shared theme + box-drawing helper for TUI overlays.
27
+ *
28
+ * Usage:
29
+ * ```ts
30
+ * class MyOverlay implements Component {
31
+ * private overlay = new OverlayTheme();
32
+ *
33
+ * setTheme(theme: Theme) { this.overlay.setTheme(theme); }
34
+ *
35
+ * render(width: number): string[] {
36
+ * const inner = boxInnerWidth(width);
37
+ * return [
38
+ * this.overlay.borderLine(inner, "top"),
39
+ * this.overlay.frameLine("Title", inner),
40
+ * this.overlay.borderLine(inner, "bottom"),
41
+ * ];
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ export class OverlayTheme {
47
+ private theme: Theme | null = null;
48
+
49
+ setTheme(theme: Theme | null): void {
50
+ this.theme = theme;
51
+ }
52
+
53
+ /** Color text using the active theme, or a fallback ANSI code. */
54
+ fg(color: string, text: string): string {
55
+ if (this.theme) return this.theme.fg(color as never, text);
56
+ return `${FALLBACK_COLORS[color] ?? ""}${text}\x1b[0m`;
57
+ }
58
+
59
+ /** Bold text using the active theme, or a fallback ANSI code. */
60
+ bold(text: string): string {
61
+ if (this.theme) return this.theme.bold(text);
62
+ return `\x1b[1m${text}\x1b[0m`;
63
+ }
64
+
65
+ /** Background color using the active theme, or text unchanged. */
66
+ bg(color: string, text: string): string {
67
+ if (this.theme) return this.theme.bg(color as never, text);
68
+ return text;
69
+ }
70
+
71
+ /** Frame a content line with `│` borders, padded and truncated to innerWidth. */
72
+ frameLine(content: string, innerWidth: number): string {
73
+ const truncated = truncateToWidth(content, innerWidth, "");
74
+ const padding = Math.max(0, innerWidth - visibleWidth(truncated));
75
+ return `${this.fg("borderMuted", "│")}${truncated}${safeRepeat(" ", padding)}${this.fg("borderMuted", "│")}`;
76
+ }
77
+
78
+ /** Horizontal rule: `├───┤`. */
79
+ ruleLine(innerWidth: number): string {
80
+ return this.fg("borderMuted", `├${safeRepeat("─", innerWidth)}┤`);
81
+ }
82
+
83
+ /** Top or bottom border: `┌───┐` / `└───┘`. */
84
+ borderLine(innerWidth: number, edge: "top" | "bottom"): string {
85
+ const left = edge === "top" ? "┌" : "└";
86
+ const right = edge === "top" ? "┐" : "┘";
87
+ return this.fg("borderMuted", `${left}${safeRepeat("─", innerWidth)}${right}`);
88
+ }
89
+ }
package/tui-width.ts CHANGED
@@ -125,3 +125,21 @@ export class WidthKeyedCache {
125
125
  this.width = -1;
126
126
  }
127
127
  }
128
+
129
+ /** Shared ANSI escape codes for TUI overlays. */
130
+ export const ansi = {
131
+ reset: "\x1b[0m",
132
+ bold: "\x1b[1m",
133
+ dim: "\x1b[2m",
134
+ cyan: "\x1b[36m",
135
+ green: "\x1b[32m",
136
+ yellow: "\x1b[33m",
137
+ red: "\x1b[31m",
138
+ gray: "\x1b[90m",
139
+ white: "\x1b[37m",
140
+ blue: "\x1b[34m",
141
+ };
142
+
143
+ /** Toggle symbols for TUI settings overlays. */
144
+ export const TOGGLE_ON = `${ansi.green}●${ansi.reset}`;
145
+ export const TOGGLE_OFF = `${ansi.dim}○${ansi.reset}`;
package/utils.ts CHANGED
@@ -303,3 +303,46 @@ export async function withHerdrBlocked<T>(
303
303
  event(false);
304
304
  }
305
305
  }
306
+
307
+ /** Format a token count for display (e.g. 1234 → "1.2k", 1500000 → "1.5M"). */
308
+ export function formatTokens(n: number): string {
309
+ if (n < 1000) return String(n);
310
+ if (n < 10_000) return `${(n / 1000).toFixed(1)}k`;
311
+ if (n < 1_000_000) return `${Math.round(n / 1000)}k`;
312
+ if (n < 10_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
313
+ return `${Math.round(n / 1_000_000)}M`;
314
+ }
315
+
316
+ /** Check if a snapshot is active by checking its details.active field or content. */
317
+ export function isActiveSnapshot(snapshot: { content: unknown; details?: { active?: boolean } }): boolean {
318
+ if (typeof snapshot.details?.active === "boolean") return snapshot.details.active;
319
+ return typeof snapshot.content === "string" && snapshot.content.includes("Status: active");
320
+ }
321
+
322
+ /** Compare two semver-ish version strings.
323
+ * Returns 1 when a > b, -1 when a < b, 0 when equal.
324
+ * Handles `v` prefix, splits on `.` or `-`, ignores non-numeric suffixes. */
325
+ export function compareVersions(a: string, b: string): number {
326
+ const parse = (version: string): number[] => version
327
+ .replace(/^v/, "")
328
+ .split(/[.-]/)
329
+ .slice(0, 3)
330
+ .map((part) => {
331
+ const parsed = Number.parseInt(part, 10);
332
+ return Number.isNaN(parsed) ? 0 : parsed;
333
+ });
334
+
335
+ const left = parse(a);
336
+ const right = parse(b);
337
+ for (let i = 0; i < 3; i++) {
338
+ const diff = (left[i] ?? 0) - (right[i] ?? 0);
339
+ if (diff > 0) return 1;
340
+ if (diff < 0) return -1;
341
+ }
342
+ return 0;
343
+ }
344
+
345
+ /** Return true only when `latest` is newer than `current`. */
346
+ export function isNewerVersion(latest: string, current: string): boolean {
347
+ return compareVersions(latest, current) > 0;
348
+ }