@pi-unipi/unipi 2.6.1 → 2.6.2

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 (186) hide show
  1. package/package.json +1 -2
  2. package/packages/ask-user/settings-tui.ts +18 -34
  3. package/packages/autocomplete/src/constants.ts +0 -15
  4. package/packages/autocomplete/src/provider.ts +3 -17
  5. package/packages/btw/extensions/btw.ts +18 -45
  6. package/packages/compactor/README.md +2 -2
  7. package/packages/compactor/skills/compactor/SKILL.md +1 -1
  8. package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
  9. package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
  10. package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
  11. package/packages/compactor/src/commands/index.ts +47 -78
  12. package/packages/compactor/src/compaction/brief.ts +161 -90
  13. package/packages/compactor/src/compaction/build-sections.ts +3 -4
  14. package/packages/compactor/src/compaction/compact-args.ts +86 -0
  15. package/packages/compactor/src/compaction/cut.ts +270 -28
  16. package/packages/compactor/src/compaction/drill-down.ts +261 -0
  17. package/packages/compactor/src/compaction/format-recall.ts +96 -0
  18. package/packages/compactor/src/compaction/format.ts +8 -3
  19. package/packages/compactor/src/compaction/hooks.ts +248 -72
  20. package/packages/compactor/src/compaction/merge.ts +34 -4
  21. package/packages/compactor/src/compaction/rank.ts +270 -0
  22. package/packages/compactor/src/compaction/recall-scope.ts +28 -0
  23. package/packages/compactor/src/compaction/search-entries.ts +333 -96
  24. package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
  25. package/packages/compactor/src/compaction/summarize.ts +37 -6
  26. package/packages/compactor/src/compaction/token-estimate.ts +104 -0
  27. package/packages/compactor/src/compaction/touched-files.ts +35 -0
  28. package/packages/compactor/src/config/manager.ts +2 -27
  29. package/packages/compactor/src/config/presets.ts +0 -2
  30. package/packages/compactor/src/config/schema.ts +2 -16
  31. package/packages/compactor/src/executor/executor.ts +6 -15
  32. package/packages/compactor/src/executor/runtime.ts +2 -12
  33. package/packages/compactor/src/index.ts +4 -121
  34. package/packages/compactor/src/info-screen.ts +3 -10
  35. package/packages/compactor/src/security/evaluator.ts +0 -53
  36. package/packages/compactor/src/security/policy.ts +7 -8
  37. package/packages/compactor/src/session/db.ts +0 -6
  38. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
  39. package/packages/compactor/src/tools/register.ts +27 -50
  40. package/packages/compactor/src/tools/vcc-recall.ts +86 -48
  41. package/packages/compactor/src/tui/settings-overlay.ts +20 -40
  42. package/packages/compactor/src/types.ts +43 -100
  43. package/packages/core/constants.ts +0 -28
  44. package/packages/core/events.ts +1 -83
  45. package/packages/core/index.ts +1 -0
  46. package/packages/core/package.json +2 -0
  47. package/packages/core/tui-overlay.ts +89 -0
  48. package/packages/core/tui-width.ts +18 -0
  49. package/packages/core/utils.ts +43 -0
  50. package/packages/footer/src/help.ts +2 -2
  51. package/packages/footer/src/index.ts +9 -10
  52. package/packages/footer/src/presets.ts +1 -27
  53. package/packages/footer/src/registry/index.ts +0 -23
  54. package/packages/footer/src/rendering/icons.ts +0 -13
  55. package/packages/footer/src/rendering/renderer.ts +5 -40
  56. package/packages/footer/src/rendering/separators.ts +0 -10
  57. package/packages/footer/src/rendering/theme.ts +1 -15
  58. package/packages/footer/src/segments/compactor.ts +7 -14
  59. package/packages/footer/src/segments/core.ts +15 -54
  60. package/packages/footer/src/segments/kanboard.ts +5 -5
  61. package/packages/footer/src/segments/mcp.ts +5 -5
  62. package/packages/footer/src/segments/memory.ts +4 -4
  63. package/packages/footer/src/segments/notify.ts +2 -2
  64. package/packages/footer/src/segments/ralph.ts +3 -3
  65. package/packages/footer/src/segments/status-ext.ts +1 -1
  66. package/packages/footer/src/segments/workflow.ts +3 -3
  67. package/packages/footer/src/tps-tracker.ts +0 -8
  68. package/packages/footer/src/tui/settings-tui.ts +14 -31
  69. package/packages/footer/src/types.ts +0 -15
  70. package/packages/image/src/register-providers.ts +0 -11
  71. package/packages/image/src/tools.ts +0 -3
  72. package/packages/image/src/tui/model-selector.ts +36 -71
  73. package/packages/info-screen/config.ts +0 -16
  74. package/packages/info-screen/core-groups.ts +0 -10
  75. package/packages/info-screen/index.ts +22 -13
  76. package/packages/info-screen/registry.ts +1 -72
  77. package/packages/info-screen/tui/info-overlay.ts +85 -100
  78. package/packages/info-screen/usage-parser.ts +12 -47
  79. package/packages/input-shortcuts/src/index.ts +3 -3
  80. package/packages/input-shortcuts/src/registers.ts +0 -8
  81. package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
  82. package/packages/input-shortcuts/src/undo-redo.ts +0 -12
  83. package/packages/mcp/src/bridge/client.ts +0 -15
  84. package/packages/mcp/src/config/manager.ts +0 -38
  85. package/packages/mcp/src/config/schema.ts +0 -27
  86. package/packages/mcp/src/config/sync.ts +0 -29
  87. package/packages/mcp/src/index.ts +1 -1
  88. package/packages/mcp/src/tui/add-overlay.ts +6 -6
  89. package/packages/mcp/src/types.ts +0 -6
  90. package/packages/memory/embedding.ts +0 -31
  91. package/packages/memory/mempalace.ts +0 -16
  92. package/packages/memory/package.json +2 -6
  93. package/packages/memory/storage.ts +56 -753
  94. package/packages/milestone/hooks.ts +1 -6
  95. package/packages/milestone/milestone.ts +2 -41
  96. package/packages/notify/events.ts +0 -10
  97. package/packages/notify/settings.ts +0 -19
  98. package/packages/notify/tui/gotify-setup.ts +108 -143
  99. package/packages/notify/tui/ntfy-setup.ts +137 -172
  100. package/packages/notify/tui/recap-model-selector.ts +36 -71
  101. package/packages/notify/tui/settings-overlay.ts +50 -86
  102. package/packages/notify/tui/telegram-setup.ts +48 -79
  103. package/packages/notify/types.ts +0 -2
  104. package/packages/ralph/index.ts +11 -35
  105. package/packages/ralph/ralph-loop.ts +3 -44
  106. package/packages/subagents/package.json +1 -0
  107. package/packages/subagents/src/agent-manager.ts +0 -51
  108. package/packages/subagents/src/agent-runner.ts +6 -80
  109. package/packages/subagents/src/conversation-viewer.ts +1 -19
  110. package/packages/subagents/src/core-compat.ts +8 -25
  111. package/packages/subagents/src/custom-agents.ts +0 -1
  112. package/packages/subagents/src/index.ts +3 -48
  113. package/packages/subagents/src/types.ts +0 -11
  114. package/packages/subagents/src/widget.ts +5 -5
  115. package/packages/unipi/bundled.js +4330 -7707
  116. package/packages/unipi/index.ts +6 -3
  117. package/packages/updater/src/changelog.ts +1 -16
  118. package/packages/updater/src/index.ts +0 -5
  119. package/packages/updater/src/installer.ts +4 -26
  120. package/packages/updater/src/markdown.ts +6 -152
  121. package/packages/updater/src/settings.ts +0 -15
  122. package/packages/updater/src/tui/changelog-overlay.ts +42 -234
  123. package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
  124. package/packages/updater/src/tui/readme-overlay.ts +53 -210
  125. package/packages/updater/src/tui/settings-overlay.ts +13 -26
  126. package/packages/updater/src/version.ts +2 -31
  127. package/packages/utility/package.json +1 -3
  128. package/packages/utility/src/commands.ts +4 -5
  129. package/packages/utility/src/herdr-sync.ts +136 -0
  130. package/packages/utility/src/index.ts +11 -68
  131. package/packages/utility/src/lifecycle/cleanup.ts +2 -16
  132. package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
  133. package/packages/utility/src/tools/env.ts +0 -0
  134. package/packages/utility/src/tui/name-badge-state.ts +18 -2
  135. package/packages/utility/src/tui/name-badge.ts +5 -4
  136. package/packages/utility/src/tui/util-settings-tui.ts +41 -224
  137. package/packages/web-api/package.json +0 -4
  138. package/packages/web-api/src/engine/dependencies.ts +13 -134
  139. package/packages/web-api/src/engine/extract.ts +9 -99
  140. package/packages/web-api/src/engine/format.ts +0 -75
  141. package/packages/web-api/src/engine/types.ts +0 -39
  142. package/packages/web-api/src/index.ts +1 -2
  143. package/packages/web-api/src/providers/base.ts +0 -10
  144. package/packages/web-api/src/providers/duckduckgo.ts +0 -1
  145. package/packages/web-api/src/providers/firecrawl.ts +0 -9
  146. package/packages/web-api/src/providers/jina-reader.ts +0 -9
  147. package/packages/web-api/src/providers/jina-search.ts +0 -9
  148. package/packages/web-api/src/providers/perplexity.ts +0 -9
  149. package/packages/web-api/src/providers/registry.ts +0 -80
  150. package/packages/web-api/src/providers/serpapi.ts +0 -9
  151. package/packages/web-api/src/providers/tavily.ts +0 -9
  152. package/packages/web-api/src/providers/wigolo-client.ts +0 -30
  153. package/packages/web-api/src/providers/wigolo.ts +0 -1
  154. package/packages/web-api/src/settings.ts +17 -77
  155. package/packages/web-api/src/tools.ts +21 -24
  156. package/packages/web-api/src/tui/settings-dialog.ts +0 -1
  157. package/packages/workflow/commands.ts +38 -122
  158. package/packages/workflow/index.ts +1 -5
  159. package/packages/cocoindex/README.md +0 -94
  160. package/packages/cocoindex/bridge.ts +0 -842
  161. package/packages/cocoindex/commands.ts +0 -175
  162. package/packages/cocoindex/index.ts +0 -48
  163. package/packages/cocoindex/installer.ts +0 -397
  164. package/packages/cocoindex/package.json +0 -49
  165. package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
  166. package/packages/cocoindex/tools.ts +0 -157
  167. package/packages/compactor/src/display/diff-renderer.ts +0 -281
  168. package/packages/compactor/src/display/line-width-safety.ts +0 -28
  169. package/packages/compactor/src/display/render-utils.ts +0 -52
  170. package/packages/compactor/src/display/thinking-label.ts +0 -18
  171. package/packages/compactor/src/display/tool-overrides.ts +0 -136
  172. package/packages/compactor/src/tools/compact.ts +0 -20
  173. package/packages/subagents/src/file-lock.ts +0 -102
  174. package/packages/utility/src/cache/ttl-cache.ts +0 -311
  175. package/packages/utility/src/diff/highlighter.ts +0 -352
  176. package/packages/utility/src/diff/parser.ts +0 -191
  177. package/packages/utility/src/diff/renderer.ts +0 -395
  178. package/packages/utility/src/diff/theme.ts +0 -316
  179. package/packages/utility/src/diff/wrapper.ts +0 -339
  180. package/packages/utility/src/display/capabilities.ts +0 -214
  181. package/packages/utility/src/display/width.ts +0 -226
  182. package/packages/utility/src/tools/batch.ts +0 -229
  183. package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
  184. package/packages/utility/src/tui/badge-settings.ts +0 -67
  185. package/packages/utility/src/tui/settings-inspector.ts +0 -303
  186. package/packages/web-api/src/providers/llm-summarize.ts +0 -71
@@ -1,352 +0,0 @@
1
- /**
2
- * @pi-unipi/utility — Shiki Highlighter
3
- *
4
- * Singleton Shiki ANSI highlighter with LRU cache, language detection,
5
- * and contrast normalization.
6
- */
7
-
8
- // ─── Constants ──────────────────────────────────────────────────────────────────
9
-
10
- /** Maximum number of cached highlight results */
11
- export const CACHE_LIMIT = 192;
12
-
13
- /** Maximum characters to highlight (skip Shiki above this) */
14
- export const MAX_HL_CHARS = 80_000;
15
-
16
- // ─── LRU Cache ──────────────────────────────────────────────────────────────────
17
-
18
- /**
19
- * Simple LRU cache with string keys.
20
- * Evicts oldest entries when capacity is reached.
21
- */
22
- export class LruCache<V> {
23
- private map = new Map<string, V>();
24
- private capacity: number;
25
-
26
- constructor(capacity: number) {
27
- this.capacity = capacity;
28
- }
29
-
30
- get(key: string): V | undefined {
31
- const value = this.map.get(key);
32
- if (value !== undefined) {
33
- // Move to end (most recently used)
34
- this.map.delete(key);
35
- this.map.set(key, value);
36
- }
37
- return value;
38
- }
39
-
40
- set(key: string, value: V): void {
41
- if (this.map.has(key)) {
42
- this.map.delete(key);
43
- } else if (this.map.size >= this.capacity) {
44
- // Evict oldest (first entry)
45
- const firstKey = this.map.keys().next().value;
46
- if (firstKey !== undefined) {
47
- this.map.delete(firstKey);
48
- }
49
- }
50
- this.map.set(key, value);
51
- }
52
-
53
- has(key: string): boolean {
54
- return this.map.has(key);
55
- }
56
-
57
- get size(): number {
58
- return this.map.size;
59
- }
60
-
61
- clear(): void {
62
- this.map.clear();
63
- }
64
- }
65
-
66
- // ─── Language Detection ─────────────────────────────────────────────────────────
67
-
68
- /** File extension → Shiki language mapping */
69
- export const EXT_LANG: Record<string, string> = {
70
- ".ts": "typescript",
71
- ".tsx": "tsx",
72
- ".js": "javascript",
73
- ".jsx": "jsx",
74
- ".mjs": "javascript",
75
- ".cjs": "javascript",
76
- ".mts": "typescript",
77
- ".cts": "typescript",
78
- ".json": "json",
79
- ".jsonc": "jsonc",
80
- ".json5": "json5",
81
- ".html": "html",
82
- ".htm": "html",
83
- ".css": "css",
84
- ".scss": "scss",
85
- ".sass": "sass",
86
- ".less": "less",
87
- ".md": "markdown",
88
- ".mdx": "mdx",
89
- ".py": "python",
90
- ".rb": "ruby",
91
- ".go": "go",
92
- ".rs": "rust",
93
- ".java": "java",
94
- ".kt": "kotlin",
95
- ".kts": "kotlin",
96
- ".c": "c",
97
- ".h": "c",
98
- ".cpp": "cpp",
99
- ".hpp": "cpp",
100
- ".cc": "cpp",
101
- ".cs": "csharp",
102
- ".swift": "swift",
103
- ".php": "php",
104
- ".sql": "sql",
105
- ".sh": "bash",
106
- ".bash": "bash",
107
- ".zsh": "bash",
108
- ".fish": "fish",
109
- ".yaml": "yaml",
110
- ".yml": "yaml",
111
- ".toml": "toml",
112
- ".xml": "xml",
113
- ".svg": "xml",
114
- ".graphql": "graphql",
115
- ".gql": "graphql",
116
- ".vue": "vue",
117
- ".svelte": "svelte",
118
- ".astro": "astro",
119
- ".prisma": "prisma",
120
- ".dockerfile": "dockerfile",
121
- ".tf": "hcl",
122
- ".hcl": "hcl",
123
- ".lua": "lua",
124
- ".r": "r",
125
- ".R": "r",
126
- ".dart": "dart",
127
- ".ex": "elixir",
128
- ".exs": "elixir",
129
- ".erl": "erlang",
130
- ".hrl": "erlang",
131
- ".clj": "clojure",
132
- ".cljs": "clojure",
133
- ".hs": "haskell",
134
- ".elm": "elm",
135
- ".nim": "nim",
136
- ".zig": "zig",
137
- ".v": "v",
138
- ".jl": "julia",
139
- ".ml": "ocaml",
140
- ".mli": "ocaml",
141
- ".fs": "fsharp",
142
- ".fsx": "fsharp",
143
- ".fsi": "fsharp",
144
- };
145
-
146
- /**
147
- * Detect the Shiki language from a file extension.
148
- * Returns "text" if the extension is unknown.
149
- */
150
- export function detectLanguage(extension: string): string {
151
- const ext = extension.startsWith(".") ? extension.toLowerCase() : `.${extension.toLowerCase()}`;
152
- return EXT_LANG[ext] ?? "text";
153
- }
154
-
155
- /**
156
- * Detect language from a file path.
157
- */
158
- export function detectLanguageFromPath(filePath: string): string {
159
- const ext = filePath.lastIndexOf(".") >= 0 ? filePath.substring(filePath.lastIndexOf(".")) : "";
160
- return detectLanguage(ext);
161
- }
162
-
163
- // ─── Contrast Normalization ─────────────────────────────────────────────────────
164
-
165
- /**
166
- * Calculate the relative luminance of a hex color.
167
- * Used for contrast ratio calculations.
168
- */
169
- function relativeLuminance(hex: string): number {
170
- const h = hex.replace(/^#/, "");
171
- const r = parseInt(h.substring(0, 2), 16) / 255;
172
- const g = parseInt(h.substring(2, 4), 16) / 255;
173
- const b = parseInt(h.substring(4, 6), 16) / 255;
174
-
175
- const toLinear = (c: number) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
176
- return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
177
- }
178
-
179
- /**
180
- * Calculate contrast ratio between two colors.
181
- */
182
- function contrastRatio(fg: string, bg: string): number {
183
- const l1 = relativeLuminance(fg);
184
- const l2 = relativeLuminance(bg);
185
- const lighter = Math.max(l1, l2);
186
- const darker = Math.min(l1, l2);
187
- return (lighter + 0.05) / (darker + 0.05);
188
- }
189
-
190
- /**
191
- * Extract the foreground color from an ANSI 24-bit escape sequence.
192
- * Returns the hex color and the full match for replacement.
193
- */
194
- function extractAnsiFg(ansi: string): { hex: string; match: string } | null {
195
- const match = ansi.match(/\x1b\[38;2;(\d+);(\d+);(\d+)m/);
196
- if (!match) return null;
197
- const r = parseInt(match[1]);
198
- const g = parseInt(match[2]);
199
- const b = parseInt(match[3]);
200
- const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
201
- return { hex, match: match[0] };
202
- }
203
-
204
- /**
205
- * Normalize low-contrast Shiki foregrounds against a dark background.
206
- *
207
- * Shiki themes sometimes produce foreground colors with poor contrast
208
- * against diff backgrounds. This function bumps the brightness of any
209
- * foreground that falls below the minimum contrast ratio.
210
- *
211
- * @param ansi - ANSI string with 24-bit color codes
212
- * @param bgHex - Background hex color to test against (default: dark bg)
213
- * @param minRatio - Minimum contrast ratio (default: 3.0)
214
- */
215
- export function normalizeShikiContrast(
216
- ansi: string,
217
- bgHex: string = "#1a1a2e",
218
- minRatio: number = 3.0,
219
- ): string {
220
- // Find all 24-bit foreground sequences
221
- const fgRegex = /\x1b\[38;2;\d+;\d+;\d+m/g;
222
- let result = ansi;
223
- let match: RegExpExecArray | null;
224
-
225
- while ((match = fgRegex.exec(ansi)) !== null) {
226
- const fgInfo = extractAnsiFg(match[0] + ansi.substring(match.index + match[0].length));
227
- if (!fgInfo) continue;
228
-
229
- const ratio = contrastRatio(fgInfo.hex, bgHex);
230
- if (ratio < minRatio) {
231
- // Brighten the foreground by mixing with white
232
- const [r, g, b] = [
233
- parseInt(fgInfo.hex.slice(1, 3), 16),
234
- parseInt(fgInfo.hex.slice(3, 5), 16),
235
- parseInt(fgInfo.hex.slice(5, 7), 16),
236
- ];
237
- const factor = minRatio / Math.max(ratio, 0.01);
238
- const nr = Math.min(255, Math.round(r + (255 - r) * Math.min(1, factor * 0.5)));
239
- const ng = Math.min(255, Math.round(g + (255 - g) * Math.min(1, factor * 0.5)));
240
- const nb = Math.min(255, Math.round(b + (255 - b) * Math.min(1, factor * 0.5)));
241
- const newFg = `\x1b[38;2;${nr};${ng};${nb}m`;
242
- result = result.replace(match[0], newFg);
243
- }
244
- }
245
-
246
- return result;
247
- }
248
-
249
- // ─── Shiki Highlighter ──────────────────────────────────────────────────────────
250
-
251
- /** Shiki highlighter instance (lazy singleton) */
252
- let shikiHighlighter: import("shiki").Highlighter | null = null;
253
- let shikiInitPromise: Promise<any> | null = null;
254
-
255
- /**
256
- * Initialize the Shiki highlighter (singleton).
257
- * Returns the highlighter instance.
258
- */
259
- export async function getShikiHighlighter(): Promise<any> {
260
- if (shikiHighlighter) return shikiHighlighter;
261
- if (shikiInitPromise) return shikiInitPromise;
262
-
263
- shikiInitPromise = (async () => {
264
- try {
265
- const { createHighlighter } = await import("shiki");
266
- shikiHighlighter = await createHighlighter({
267
- themes: ["github-dark"],
268
- langs: [
269
- "typescript", "javascript", "tsx", "jsx", "json", "jsonc",
270
- "html", "css", "scss", "markdown", "python", "go", "rust",
271
- "java", "c", "cpp", "csharp", "ruby", "php", "swift", "kotlin",
272
- "bash", "yaml", "toml", "xml", "sql", "graphql", "vue", "svelte",
273
- ],
274
- });
275
- return shikiHighlighter;
276
- } catch {
277
- // If Shiki fails to load, return null — we'll use plain text
278
- shikiInitPromise = null;
279
- return null;
280
- }
281
- })();
282
-
283
- return shikiInitPromise;
284
- }
285
-
286
- /**
287
- * Pre-warm the Shiki highlighter.
288
- * Call this early in the extension lifecycle to avoid first-render delay.
289
- */
290
- export async function preWarmHighlighter(): Promise<void> {
291
- await getShikiHighlighter();
292
- }
293
-
294
- /** LRU cache for highlighted blocks */
295
- const hlCache = new LruCache<string[]>(CACHE_LIMIT);
296
-
297
- /**
298
- * Generate a cache key for a code block.
299
- */
300
- function hlCacheKey(code: string, language: string): string {
301
- // Use first 200 chars + length + language for cache key
302
- const prefix = code.substring(0, 200);
303
- return `${language}:${code.length}:${prefix}`;
304
- }
305
-
306
- /**
307
- * Highlight a code block to ANSI using Shiki.
308
- * Results are cached in an LRU cache (192 entries).
309
- *
310
- * @param code - Code to highlight
311
- * @param language - Shiki language identifier
312
- * @returns Array of ANSI-highlighted lines, or plain lines if Shiki unavailable
313
- */
314
- export async function hlBlock(code: string, language: string): Promise<string[]> {
315
- // Skip highlighting for very large content
316
- if (code.length > MAX_HL_CHARS) {
317
- return code.split("\n");
318
- }
319
-
320
- // Check cache
321
- const key = hlCacheKey(code, language);
322
- const cached = hlCache.get(key);
323
- if (cached) return cached;
324
-
325
- // Get highlighter
326
- const highlighter = await getShikiHighlighter();
327
- if (!highlighter) {
328
- // Shiki not available — return plain text
329
- return code.split("\n");
330
- }
331
-
332
- try {
333
- // Highlight with Shiki
334
- const ansi = highlighter.codeToANSI(code, {
335
- lang: language === "text" ? "text" : language,
336
- theme: "github-dark",
337
- });
338
-
339
- const lines = ansi.split("\n");
340
-
341
- // Normalize contrast
342
- const normalized = lines.map((line: string) => normalizeShikiContrast(line));
343
-
344
- // Cache result
345
- hlCache.set(key, normalized);
346
-
347
- return normalized;
348
- } catch {
349
- // Fallback on error
350
- return code.split("\n");
351
- }
352
- }
@@ -1,191 +0,0 @@
1
- /**
2
- * @pi-unipi/utility — Diff Parser
3
- *
4
- * Parses unified diffs from structuredPatch and provides word-level diff analysis.
5
- * Wraps the `diff` library for structured output.
6
- */
7
-
8
- import * as Diff from "diff";
9
-
10
- // ─── Types ──────────────────────────────────────────────────────────────────────
11
-
12
- /** A single line in a parsed diff */
13
- export interface DiffLine {
14
- /** Line type: added, removed, context, or hunk separator */
15
- type: "add" | "remove" | "context" | "hunk";
16
- /** Original line number (null for added lines) */
17
- oldLine: number | null;
18
- /** New line number (null for removed lines) */
19
- newLine: number | null;
20
- /** Line content (without +/- prefix) */
21
- content: string;
22
- }
23
-
24
- /** Result of parsing a diff */
25
- export interface ParsedDiff {
26
- /** Old file name */
27
- oldName: string;
28
- /** New file name */
29
- newName: string;
30
- /** Parsed lines */
31
- lines: DiffLine[];
32
- /** Total additions */
33
- additions: number;
34
- /** Total deletions */
35
- deletions: number;
36
- }
37
-
38
- /** Word-level diff result */
39
- export interface WordDiffResult {
40
- /** Similarity score (0-1, where 1 = identical) */
41
- similarity: number;
42
- /** Changed ranges in the new text */
43
- addedRanges: Array<{ start: number; end: number }>;
44
- /** Changed ranges in the old text */
45
- removedRanges: Array<{ start: number; end: number }>;
46
- }
47
-
48
- // ─── Diff Parsing ───────────────────────────────────────────────────────────────
49
-
50
- /**
51
- * Parse a unified diff from two content strings.
52
- * Returns structured DiffLine[] with line numbers and hunk separators.
53
- *
54
- * @param oldContent - Original content
55
- * @param newContent - Modified content
56
- * @param context - Number of context lines around changes (default 3)
57
- * @param oldName - Label for old file
58
- * @param newName - Label for new file
59
- */
60
- export function parseDiff(
61
- oldContent: string,
62
- newContent: string,
63
- context: number = 3,
64
- oldName: string = "old",
65
- newName: string = "new",
66
- ): ParsedDiff {
67
- // Handle edge cases
68
- if (oldContent === newContent) {
69
- return { oldName, newName, lines: [], additions: 0, deletions: 0 };
70
- }
71
-
72
- if (oldContent === "") {
73
- // New file — all lines are additions
74
- const lines = newContent.split("\n");
75
- const diffLines: DiffLine[] = lines.map((content, i) => ({
76
- type: "add" as const,
77
- oldLine: null,
78
- newLine: i + 1,
79
- content,
80
- }));
81
- return { oldName, newName, lines: diffLines, additions: lines.length, deletions: 0 };
82
- }
83
-
84
- if (newContent === "") {
85
- // Deleted file — all lines are removals
86
- const lines = oldContent.split("\n");
87
- const diffLines: DiffLine[] = lines.map((content, i) => ({
88
- type: "remove" as const,
89
- oldLine: i + 1,
90
- newLine: null,
91
- content,
92
- }));
93
- return { oldName, newName, lines: diffLines, additions: 0, deletions: lines.length };
94
- }
95
-
96
- // Use structuredPatch for unified diff
97
- const patches = Diff.structuredPatch(oldName, newName, oldContent, newContent, "", "", { context });
98
-
99
- const lines: DiffLine[] = [];
100
- let additions = 0;
101
- let deletions = 0;
102
-
103
- for (const hunk of patches.hunks) {
104
- // Add hunk separator
105
- lines.push({
106
- type: "hunk",
107
- oldLine: hunk.oldStart,
108
- newLine: hunk.newStart,
109
- content: `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`,
110
- });
111
-
112
- let oldLine = hunk.oldStart;
113
- let newLine = hunk.newStart;
114
-
115
- for (const line of hunk.lines) {
116
- const prefix = line[0];
117
- const content = line.substring(1);
118
-
119
- if (prefix === "+") {
120
- lines.push({ type: "add", oldLine: null, newLine, content });
121
- additions++;
122
- newLine++;
123
- } else if (prefix === "-") {
124
- lines.push({ type: "remove", oldLine, newLine: null, content });
125
- deletions++;
126
- oldLine++;
127
- } else {
128
- // Context line (space prefix)
129
- lines.push({ type: "context", oldLine, newLine, content });
130
- oldLine++;
131
- newLine++;
132
- }
133
- }
134
- }
135
-
136
- return { oldName, newName, lines, additions, deletions };
137
- }
138
-
139
- /**
140
- * Word-level diff analysis between two strings.
141
- * Returns similarity score and character ranges of changes.
142
- */
143
- export function wordDiffAnalysis(a: string, b: string): WordDiffResult {
144
- // Fast path: identical strings
145
- if (a === b) {
146
- return { similarity: 1, addedRanges: [], removedRanges: [] };
147
- }
148
-
149
- // Fast path: empty strings
150
- if (a.length === 0 && b.length === 0) {
151
- return { similarity: 1, addedRanges: [], removedRanges: [] };
152
- }
153
- if (a.length === 0) {
154
- return { similarity: 0, addedRanges: [{ start: 0, end: b.length }], removedRanges: [] };
155
- }
156
- if (b.length === 0) {
157
- return { similarity: 0, addedRanges: [], removedRanges: [{ start: 0, end: a.length }] };
158
- }
159
-
160
- const changes = Diff.diffWords(a, b);
161
-
162
- const addedRanges: Array<{ start: number; end: number }> = [];
163
- const removedRanges: Array<{ start: number; end: number }> = [];
164
- let aPos = 0;
165
- let bPos = 0;
166
- let unchangedChars = 0;
167
-
168
- for (const change of changes) {
169
- if (change.added) {
170
- addedRanges.push({ start: bPos, end: bPos + (change.value?.length ?? 0) });
171
- bPos += change.value?.length ?? 0;
172
- } else if (change.removed) {
173
- removedRanges.push({ start: aPos, end: aPos + (change.value?.length ?? 0) });
174
- aPos += change.value?.length ?? 0;
175
- } else {
176
- // Unchanged
177
- unchangedChars += change.value?.length ?? 0;
178
- aPos += change.value?.length ?? 0;
179
- bPos += change.value?.length ?? 0;
180
- }
181
- }
182
-
183
- const totalChars = Math.max(a.length + b.length, 1);
184
- const similarity = (unchangedChars * 2) / totalChars;
185
-
186
- return {
187
- similarity: Math.min(1, Math.max(0, similarity)),
188
- addedRanges,
189
- removedRanges,
190
- };
191
- }