@pi-unipi/unipi 2.6.0 → 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 (205) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +21 -22
  3. package/packages/ask-user/package.json +2 -2
  4. package/packages/ask-user/settings-tui.ts +18 -34
  5. package/packages/autocomplete/package.json +1 -1
  6. package/packages/autocomplete/src/constants.ts +2 -15
  7. package/packages/autocomplete/src/provider.ts +3 -17
  8. package/packages/btw/extensions/btw.ts +18 -45
  9. package/packages/btw/package.json +2 -2
  10. package/packages/compactor/README.md +2 -2
  11. package/packages/compactor/package.json +3 -3
  12. package/packages/compactor/skills/compactor/SKILL.md +1 -1
  13. package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
  14. package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
  15. package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
  16. package/packages/compactor/src/commands/index.ts +47 -78
  17. package/packages/compactor/src/compaction/brief.ts +161 -90
  18. package/packages/compactor/src/compaction/build-sections.ts +3 -4
  19. package/packages/compactor/src/compaction/compact-args.ts +86 -0
  20. package/packages/compactor/src/compaction/cut.ts +270 -28
  21. package/packages/compactor/src/compaction/drill-down.ts +261 -0
  22. package/packages/compactor/src/compaction/format-recall.ts +96 -0
  23. package/packages/compactor/src/compaction/format.ts +8 -3
  24. package/packages/compactor/src/compaction/hooks.ts +248 -72
  25. package/packages/compactor/src/compaction/merge.ts +34 -4
  26. package/packages/compactor/src/compaction/rank.ts +270 -0
  27. package/packages/compactor/src/compaction/recall-scope.ts +28 -0
  28. package/packages/compactor/src/compaction/search-entries.ts +333 -96
  29. package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
  30. package/packages/compactor/src/compaction/summarize.ts +37 -6
  31. package/packages/compactor/src/compaction/token-estimate.ts +104 -0
  32. package/packages/compactor/src/compaction/touched-files.ts +35 -0
  33. package/packages/compactor/src/config/manager.ts +2 -27
  34. package/packages/compactor/src/config/presets.ts +0 -2
  35. package/packages/compactor/src/config/schema.ts +2 -16
  36. package/packages/compactor/src/executor/executor.ts +6 -15
  37. package/packages/compactor/src/executor/runtime.ts +2 -12
  38. package/packages/compactor/src/index.ts +4 -121
  39. package/packages/compactor/src/info-screen.ts +3 -10
  40. package/packages/compactor/src/security/evaluator.ts +0 -53
  41. package/packages/compactor/src/security/policy.ts +7 -8
  42. package/packages/compactor/src/session/db.ts +0 -6
  43. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
  44. package/packages/compactor/src/tools/register.ts +27 -50
  45. package/packages/compactor/src/tools/vcc-recall.ts +86 -48
  46. package/packages/compactor/src/tui/settings-overlay.ts +20 -40
  47. package/packages/compactor/src/types.ts +43 -100
  48. package/packages/core/constants.ts +1 -28
  49. package/packages/core/events.ts +1 -83
  50. package/packages/core/index.ts +1 -0
  51. package/packages/core/model-cache.ts +18 -12
  52. package/packages/core/package.json +3 -1
  53. package/packages/core/tui-overlay.ts +89 -0
  54. package/packages/core/tui-width.ts +18 -0
  55. package/packages/core/utils.ts +43 -0
  56. package/packages/footer/package.json +2 -2
  57. package/packages/footer/src/help.ts +2 -2
  58. package/packages/footer/src/index.ts +9 -10
  59. package/packages/footer/src/presets.ts +1 -27
  60. package/packages/footer/src/registry/index.ts +0 -23
  61. package/packages/footer/src/rendering/icons.ts +0 -13
  62. package/packages/footer/src/rendering/renderer.ts +5 -40
  63. package/packages/footer/src/rendering/separators.ts +0 -10
  64. package/packages/footer/src/rendering/theme.ts +1 -15
  65. package/packages/footer/src/segments/compactor.ts +7 -14
  66. package/packages/footer/src/segments/core.ts +15 -54
  67. package/packages/footer/src/segments/kanboard.ts +5 -5
  68. package/packages/footer/src/segments/mcp.ts +5 -5
  69. package/packages/footer/src/segments/memory.ts +4 -4
  70. package/packages/footer/src/segments/notify.ts +2 -2
  71. package/packages/footer/src/segments/ralph.ts +3 -3
  72. package/packages/footer/src/segments/status-ext.ts +1 -1
  73. package/packages/footer/src/segments/workflow.ts +3 -3
  74. package/packages/footer/src/tps-tracker.ts +0 -8
  75. package/packages/footer/src/tui/settings-tui.ts +14 -31
  76. package/packages/footer/src/types.ts +0 -15
  77. package/packages/image/package.json +2 -2
  78. package/packages/image/src/register-providers.ts +0 -11
  79. package/packages/image/src/tools.ts +0 -3
  80. package/packages/image/src/tui/model-selector.ts +36 -71
  81. package/packages/info-screen/config.ts +0 -16
  82. package/packages/info-screen/core-groups.ts +0 -10
  83. package/packages/info-screen/index.ts +22 -13
  84. package/packages/info-screen/package.json +2 -2
  85. package/packages/info-screen/registry.ts +1 -72
  86. package/packages/info-screen/tui/info-overlay.ts +85 -100
  87. package/packages/info-screen/usage-parser.ts +12 -47
  88. package/packages/input-shortcuts/package.json +2 -2
  89. package/packages/input-shortcuts/src/index.ts +3 -3
  90. package/packages/input-shortcuts/src/registers.ts +0 -8
  91. package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
  92. package/packages/input-shortcuts/src/undo-redo.ts +0 -12
  93. package/packages/kanboard/package.json +2 -2
  94. package/packages/mcp/package.json +2 -2
  95. package/packages/mcp/src/bridge/client.ts +0 -15
  96. package/packages/mcp/src/config/manager.ts +0 -38
  97. package/packages/mcp/src/config/schema.ts +0 -27
  98. package/packages/mcp/src/config/sync.ts +0 -29
  99. package/packages/mcp/src/index.ts +1 -1
  100. package/packages/mcp/src/tui/add-overlay.ts +6 -6
  101. package/packages/mcp/src/types.ts +0 -6
  102. package/packages/memory/embedding.ts +0 -31
  103. package/packages/memory/mempalace.ts +0 -16
  104. package/packages/memory/package.json +4 -8
  105. package/packages/memory/storage.ts +56 -753
  106. package/packages/milestone/hooks.ts +1 -6
  107. package/packages/milestone/milestone.ts +2 -41
  108. package/packages/milestone/package.json +2 -2
  109. package/packages/notify/README.md +1 -0
  110. package/packages/notify/commands.ts +61 -3
  111. package/packages/notify/events.ts +0 -10
  112. package/packages/notify/package.json +2 -2
  113. package/packages/notify/settings.ts +0 -19
  114. package/packages/notify/tui/gotify-setup.ts +108 -143
  115. package/packages/notify/tui/ntfy-setup.ts +137 -172
  116. package/packages/notify/tui/recap-model-selector.ts +107 -101
  117. package/packages/notify/tui/settings-overlay.ts +94 -117
  118. package/packages/notify/tui/telegram-setup.ts +48 -79
  119. package/packages/notify/types.ts +0 -2
  120. package/packages/ralph/index.ts +11 -35
  121. package/packages/ralph/package.json +3 -3
  122. package/packages/ralph/ralph-loop.ts +3 -44
  123. package/packages/subagents/package.json +2 -1
  124. package/packages/subagents/src/agent-manager.ts +0 -51
  125. package/packages/subagents/src/agent-runner.ts +6 -80
  126. package/packages/subagents/src/conversation-viewer.ts +1 -19
  127. package/packages/subagents/src/core-compat.ts +8 -25
  128. package/packages/subagents/src/custom-agents.ts +0 -1
  129. package/packages/subagents/src/index.ts +3 -48
  130. package/packages/subagents/src/types.ts +0 -11
  131. package/packages/subagents/src/widget.ts +5 -5
  132. package/packages/unipi/bundled.js +4468 -7769
  133. package/packages/unipi/index.ts +6 -3
  134. package/packages/updater/package.json +2 -2
  135. package/packages/updater/src/changelog.ts +1 -16
  136. package/packages/updater/src/index.ts +0 -5
  137. package/packages/updater/src/installer.ts +4 -26
  138. package/packages/updater/src/markdown.ts +6 -152
  139. package/packages/updater/src/settings.ts +0 -15
  140. package/packages/updater/src/tui/changelog-overlay.ts +42 -234
  141. package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
  142. package/packages/updater/src/tui/readme-overlay.ts +53 -210
  143. package/packages/updater/src/tui/settings-overlay.ts +13 -26
  144. package/packages/updater/src/version.ts +2 -31
  145. package/packages/utility/package.json +2 -4
  146. package/packages/utility/src/commands.ts +4 -5
  147. package/packages/utility/src/herdr-sync.ts +136 -0
  148. package/packages/utility/src/index.ts +11 -68
  149. package/packages/utility/src/lifecycle/cleanup.ts +2 -16
  150. package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
  151. package/packages/utility/src/tools/env.ts +0 -0
  152. package/packages/utility/src/tui/name-badge-state.ts +18 -2
  153. package/packages/utility/src/tui/name-badge.ts +5 -4
  154. package/packages/utility/src/tui/util-settings-tui.ts +41 -224
  155. package/packages/web-api/package.json +2 -6
  156. package/packages/web-api/src/engine/dependencies.ts +13 -134
  157. package/packages/web-api/src/engine/extract.ts +9 -99
  158. package/packages/web-api/src/engine/format.ts +0 -75
  159. package/packages/web-api/src/engine/types.ts +0 -39
  160. package/packages/web-api/src/index.ts +1 -2
  161. package/packages/web-api/src/providers/base.ts +0 -10
  162. package/packages/web-api/src/providers/duckduckgo.ts +0 -1
  163. package/packages/web-api/src/providers/firecrawl.ts +0 -9
  164. package/packages/web-api/src/providers/jina-reader.ts +0 -9
  165. package/packages/web-api/src/providers/jina-search.ts +0 -9
  166. package/packages/web-api/src/providers/perplexity.ts +0 -9
  167. package/packages/web-api/src/providers/registry.ts +0 -80
  168. package/packages/web-api/src/providers/serpapi.ts +0 -9
  169. package/packages/web-api/src/providers/tavily.ts +0 -9
  170. package/packages/web-api/src/providers/wigolo-client.ts +0 -30
  171. package/packages/web-api/src/providers/wigolo.ts +0 -1
  172. package/packages/web-api/src/settings.ts +17 -77
  173. package/packages/web-api/src/tools.ts +21 -24
  174. package/packages/web-api/src/tui/settings-dialog.ts +0 -1
  175. package/packages/workflow/commands.ts +38 -122
  176. package/packages/workflow/index.ts +1 -5
  177. package/packages/workflow/package.json +2 -2
  178. package/packages/cocoindex/README.md +0 -94
  179. package/packages/cocoindex/bridge.ts +0 -842
  180. package/packages/cocoindex/commands.ts +0 -175
  181. package/packages/cocoindex/index.ts +0 -48
  182. package/packages/cocoindex/installer.ts +0 -397
  183. package/packages/cocoindex/package.json +0 -49
  184. package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
  185. package/packages/cocoindex/tools.ts +0 -157
  186. package/packages/compactor/src/display/diff-renderer.ts +0 -281
  187. package/packages/compactor/src/display/line-width-safety.ts +0 -28
  188. package/packages/compactor/src/display/render-utils.ts +0 -52
  189. package/packages/compactor/src/display/thinking-label.ts +0 -18
  190. package/packages/compactor/src/display/tool-overrides.ts +0 -136
  191. package/packages/compactor/src/tools/compact.ts +0 -20
  192. package/packages/subagents/src/file-lock.ts +0 -102
  193. package/packages/utility/src/cache/ttl-cache.ts +0 -311
  194. package/packages/utility/src/diff/highlighter.ts +0 -352
  195. package/packages/utility/src/diff/parser.ts +0 -191
  196. package/packages/utility/src/diff/renderer.ts +0 -395
  197. package/packages/utility/src/diff/theme.ts +0 -316
  198. package/packages/utility/src/diff/wrapper.ts +0 -339
  199. package/packages/utility/src/display/capabilities.ts +0 -214
  200. package/packages/utility/src/display/width.ts +0 -226
  201. package/packages/utility/src/tools/batch.ts +0 -229
  202. package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
  203. package/packages/utility/src/tui/badge-settings.ts +0 -67
  204. package/packages/utility/src/tui/settings-inspector.ts +0 -303
  205. package/packages/web-api/src/providers/llm-summarize.ts +0 -71
@@ -79,7 +79,6 @@ let clientPromise: Promise<WigoloLocalClient> | null = null;
79
79
  let clientOverride: WigoloClientLike | null = null;
80
80
 
81
81
  /** Last known availability, for the settings TUI and info screen. */
82
- let lastError: string | null = null;
83
82
 
84
83
  /** Load the optional SDK. Returns null when it is not installed. */
85
84
  async function loadSdk(): Promise<
@@ -127,10 +126,8 @@ export async function getWigoloClient(): Promise<WigoloClientLike> {
127
126
 
128
127
  try {
129
128
  const local = await clientPromise;
130
- lastError = null;
131
129
  return local.client;
132
130
  } catch (error) {
133
- lastError = error instanceof Error ? error.message : String(error);
134
131
  throw error;
135
132
  }
136
133
  }
@@ -149,29 +146,6 @@ export async function closeWigoloClient(): Promise<void> {
149
146
  }
150
147
 
151
148
  /** Availability for the settings TUI / info screen. Never throws. */
152
- export async function checkWigoloHealth(): Promise<{
153
- available: boolean;
154
- status: string;
155
- detail?: string;
156
- }> {
157
- const sdk = await loadSdk();
158
- if (!sdk) {
159
- return { available: false, status: "not installed", detail: NOT_INSTALLED_MESSAGE };
160
- }
161
- try {
162
- const client = await getWigoloClient();
163
- const health = await client.health();
164
- const status = typeof health?.status === "string" ? health.status : "unknown";
165
- // wigolo reports 200 "ok" when up and 503 with a body when degraded.
166
- return { available: status === "ok" || status === "healthy", status };
167
- } catch (error) {
168
- return {
169
- available: false,
170
- status: "unreachable",
171
- detail: error instanceof Error ? error.message : String(error),
172
- };
173
- }
174
- }
175
149
 
176
150
  /** Whether the SDK is importable, without starting a daemon. Never throws. */
177
151
  export async function isWigoloInstalled(): Promise<boolean> {
@@ -179,9 +153,6 @@ export async function isWigoloInstalled(): Promise<boolean> {
179
153
  }
180
154
 
181
155
  /** Last recorded failure, for diagnostics. */
182
- export function getWigoloLastError(): string | null {
183
- return lastError;
184
- }
185
156
 
186
157
  /** Inject a stub daemon client. Test-only. */
187
158
  export function __setWigoloClientForTests(client: WigoloClientLike | null): void {
@@ -192,5 +163,4 @@ export function __setWigoloClientForTests(client: WigoloClientLike | null): void
192
163
  export function __resetWigoloClientForTests(): void {
193
164
  clientPromise = null;
194
165
  clientOverride = null;
195
- lastError = null;
196
166
  }
@@ -71,7 +71,6 @@ const wigoloProvider: WebProvider = {
71
71
  read: 1,
72
72
  summarize: 0,
73
73
  },
74
- config: {},
75
74
 
76
75
  async search(query: string, config?: ProviderConfig): Promise<SearchResult[]> {
77
76
  const client = await getWigoloClient();
@@ -8,6 +8,15 @@
8
8
  import * as fs from "node:fs";
9
9
  import * as path from "node:path";
10
10
  import * as os from "node:os";
11
+ import {
12
+ DEFAULT_BROWSER,
13
+ DEFAULT_OS,
14
+ DEFAULT_MAX_CHARS,
15
+ DEFAULT_TIMEOUT_MS,
16
+ DEFAULT_BATCH_CONCURRENCY,
17
+ DEFAULT_REMOVE_IMAGES,
18
+ DEFAULT_INCLUDE_REPLIES,
19
+ } from "./engine/constants.js";
11
20
 
12
21
  /** Auth storage structure (API keys) */
13
22
  export interface WebApiAuth {
@@ -21,12 +30,6 @@ export interface ProviderSettings {
21
30
  [key: string]: unknown;
22
31
  }
23
32
 
24
- /** Cache configuration */
25
- export interface CacheSettings {
26
- enabled: boolean;
27
- ttlMs: number;
28
- }
29
-
30
33
  /** Smart-fetch default settings */
31
34
  export interface SmartFetchSettings {
32
35
  /** TLS fingerprint browser profile */
@@ -48,19 +51,18 @@ export interface SmartFetchSettings {
48
51
  /** Config storage structure */
49
52
  export interface WebApiConfig {
50
53
  providers: Record<string, ProviderSettings>;
51
- cache: CacheSettings;
52
54
  smartFetch?: Partial<SmartFetchSettings>;
53
55
  }
54
56
 
55
- /** Default smart-fetch settings */
57
+ /** Default smart-fetch settings — values from engine/constants.ts */
56
58
  const DEFAULT_SMART_FETCH_SETTINGS: SmartFetchSettings = {
57
- browser: "chrome_145",
58
- os: "windows",
59
- maxChars: 50000,
60
- timeoutMs: 15000,
61
- batchConcurrency: 8,
62
- removeImages: false,
63
- includeReplies: "extractors",
59
+ browser: DEFAULT_BROWSER,
60
+ os: DEFAULT_OS,
61
+ maxChars: DEFAULT_MAX_CHARS,
62
+ timeoutMs: DEFAULT_TIMEOUT_MS,
63
+ batchConcurrency: DEFAULT_BATCH_CONCURRENCY,
64
+ removeImages: DEFAULT_REMOVE_IMAGES,
65
+ includeReplies: DEFAULT_INCLUDE_REPLIES,
64
66
  };
65
67
 
66
68
  /** Default configuration */
@@ -76,10 +78,6 @@ const DEFAULT_CONFIG: WebApiConfig = {
76
78
  perplexity: { enabled: false },
77
79
  "llm-summarize": { enabled: true },
78
80
  },
79
- cache: {
80
- enabled: true,
81
- ttlMs: 3600000, // 1 hour
82
- },
83
81
  smartFetch: {},
84
82
  };
85
83
 
@@ -159,10 +157,6 @@ export function loadConfig(): WebApiConfig {
159
157
  ...DEFAULT_CONFIG.providers,
160
158
  ...config.providers,
161
159
  },
162
- cache: {
163
- ...DEFAULT_CONFIG.cache,
164
- ...config.cache,
165
- },
166
160
  };
167
161
  }
168
162
  } catch {
@@ -237,62 +231,8 @@ export function setProviderEnabled(providerId: string, enabled: boolean): void {
237
231
  saveConfig(config);
238
232
  }
239
233
 
240
- /**
241
- * Get cache settings.
242
- * @returns Cache configuration
243
- */
244
- export function getCacheSettings(): CacheSettings {
245
- const config = loadConfig();
246
- return config.cache;
247
- }
248
-
249
- /**
250
- * Update cache settings.
251
- * @param cache - New cache settings
252
- */
253
- export function updateCacheSettings(cache: Partial<CacheSettings>): void {
254
- const config = loadConfig();
255
- config.cache = {
256
- ...config.cache,
257
- ...cache,
258
- };
259
- saveConfig(config);
260
- }
261
234
 
262
- /**
263
- * Validate API key format (basic validation).
264
- * @param providerId - Provider ID
265
- * @param apiKey - API key to validate
266
- * @returns true if format looks valid
267
- */
268
- export function validateApiKeyFormat(providerId: string, apiKey: string): boolean {
269
- if (!apiKey || apiKey.trim().length === 0) {
270
- return false;
271
- }
272
235
 
273
- // Provider-specific format checks
274
- switch (providerId) {
275
- case "serpapi":
276
- // SerpAPI keys are typically 64 characters
277
- return apiKey.length >= 32;
278
- case "tavily":
279
- // Tavily keys start with "tvly-"
280
- return apiKey.startsWith("tvly-") && apiKey.length >= 10;
281
- case "firecrawl":
282
- // Firecrawl keys are typically longer
283
- return apiKey.length >= 20;
284
- case "perplexity":
285
- // Perplexity keys are typically longer
286
- return apiKey.length >= 20;
287
- case "jina-search":
288
- case "jina-reader":
289
- // Jina keys are typically longer
290
- return apiKey.length >= 10;
291
- default:
292
- // Generic validation
293
- return apiKey.length >= 8;
294
- }
295
- }
296
236
 
297
237
  /**
298
238
  * Load smart-fetch settings.
@@ -27,7 +27,7 @@ import {
27
27
  defuddleFetchMultiple,
28
28
  } from "./engine/extract.js";
29
29
  import type { FetchOptions, FetchResult, BatchFetchResult } from "./engine/types.js";
30
- import { formatSingleResult, formatBatchResult, formatErrorResult } from "./engine/format.js";
30
+ import { formatSingleResult, formatBatchResult } from "./engine/format.js";
31
31
 
32
32
  /** Tool names */
33
33
  export const WEB_TOOLS = {
@@ -219,6 +219,22 @@ function generateSmartFetchKey(
219
219
  return parts.join(":");
220
220
  }
221
221
 
222
+ /** Build a complete FetchOptions from partial user input + loaded defaults. */
223
+ function resolveFetchOptions(options: Partial<FetchOptions>): FetchOptions {
224
+ const defaults = loadSmartFetchSettings();
225
+ return {
226
+ browser: options.browser || defaults.browser,
227
+ os: options.os || defaults.os,
228
+ format: options.format || "markdown",
229
+ maxChars: options.maxChars || defaults.maxChars,
230
+ timeoutMs: options.timeoutMs || defaults.timeoutMs,
231
+ removeImages: options.removeImages ?? defaults.removeImages,
232
+ includeReplies: options.includeReplies ?? defaults.includeReplies,
233
+ proxy: options.proxy,
234
+ headers: options.headers,
235
+ };
236
+ }
237
+
222
238
  /**
223
239
  * Execute smart-fetch read (single URL).
224
240
  */
@@ -233,19 +249,8 @@ async function executeSmartFetchRead(
233
249
  return cached as FetchResult;
234
250
  }
235
251
 
236
- // Load defaults
237
- const defaults = loadSmartFetchSettings();
238
- const fetchOptions: FetchOptions = {
239
- browser: options.browser || defaults.browser,
240
- os: options.os || defaults.os,
241
- format: options.format || "markdown",
242
- maxChars: options.maxChars || defaults.maxChars,
243
- timeoutMs: options.timeoutMs || defaults.timeoutMs,
244
- removeImages: options.removeImages ?? defaults.removeImages,
245
- includeReplies: options.includeReplies ?? defaults.includeReplies,
246
- proxy: options.proxy,
247
- headers: options.headers,
248
- };
252
+ // Load defaults and build fetch options
253
+ const fetchOptions = resolveFetchOptions(options);
249
254
 
250
255
  // Execute fetch
251
256
  const result = await defuddleFetch(url, fetchOptions);
@@ -263,18 +268,10 @@ async function executeSmartFetchBatch(
263
268
  urls: string[],
264
269
  options: Partial<FetchOptions> & { batchConcurrency?: number } = {}
265
270
  ): Promise<BatchFetchResult> {
266
- // Load defaults
271
+ // Load defaults and build fetch options
267
272
  const defaults = loadSmartFetchSettings();
268
273
  const fetchOptions: FetchOptions & { batchConcurrency?: number } = {
269
- browser: options.browser || defaults.browser,
270
- os: options.os || defaults.os,
271
- format: options.format || "markdown",
272
- maxChars: options.maxChars || defaults.maxChars,
273
- timeoutMs: options.timeoutMs || defaults.timeoutMs,
274
- removeImages: options.removeImages ?? defaults.removeImages,
275
- includeReplies: options.includeReplies ?? defaults.includeReplies,
276
- proxy: options.proxy,
277
- headers: options.headers,
274
+ ...resolveFetchOptions(options),
278
275
  batchConcurrency: options.batchConcurrency || defaults.batchConcurrency,
279
276
  };
280
277
 
@@ -13,7 +13,6 @@ import {
13
13
  removeApiKey,
14
14
  isProviderEnabled,
15
15
  setProviderEnabled,
16
- validateApiKeyFormat,
17
16
  loadSmartFetchSettings,
18
17
  saveSmartFetchSettings,
19
18
  resetSmartFetchSettings,
@@ -8,6 +8,7 @@
8
8
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
9
  import { readFileSync, readdirSync, existsSync, statSync } from "fs";
10
10
  import { join, basename } from "path";
11
+ import { execFileSync } from "node:child_process";
11
12
  import { UNIPI_PREFIX, WORKFLOW_COMMANDS, type UnipiWorkflowEvent } from "@pi-unipi/core";
12
13
 
13
14
  type CompletionItem = { value: string; label: string; description: string };
@@ -36,111 +37,56 @@ interface WorkflowCommand {
36
37
  /**
37
38
  * Suggest spec files from .unipi/docs/specs/ for plan command.
38
39
  */
39
- function suggestSpecFiles(prefix: string): CompletionItem[] {
40
- const specsDir = join(process.cwd(), ".unipi", "docs", "specs");
41
- if (!existsSync(specsDir)) return [];
40
+ /** Suggest .md files from a docs subdirectory, sorted by mtime. */
41
+ function suggestFilesFrom(
42
+ subdir: string,
43
+ valuePrefix: string,
44
+ descLabel: string,
45
+ prefix: string,
46
+ ): CompletionItem[] {
47
+ const dir = join(process.cwd(), ".unipi", "docs", subdir);
48
+ if (!existsSync(dir)) return [];
42
49
 
43
50
  try {
44
51
  const search = prefix?.trim().split(/\s+/).pop() ?? "";
45
- const files = readdirSync(specsDir)
52
+ const files = readdirSync(dir)
46
53
  .filter((f) => f.endsWith(".md"))
47
- .map((f) => ({ name: f, time: statSync(join(specsDir, f)).mtimeMs }))
54
+ .map((f) => ({ name: f, time: statSync(join(dir, f)).mtimeMs }))
48
55
  .sort((a, b) => b.time - a.time);
49
56
  return files
50
57
  .filter((f) => !search || f.name.includes(search))
51
58
  .map((f) => ({
52
- value: `specs:${f.name}`,
59
+ value: `${valuePrefix}:${f.name}`,
53
60
  label: basename(f.name, ".md"),
54
- description: `Spec: ${f.name}`,
61
+ description: `${descLabel}: ${f.name}`,
55
62
  }));
56
63
  } catch {
57
64
  return [];
58
65
  }
59
66
  }
60
67
 
61
- /**
62
- * Suggest plan files from .unipi/docs/plans/ for work and review-work commands.
63
- */
64
- function suggestPlanFiles(prefix: string): CompletionItem[] {
65
- const plansDir = join(process.cwd(), ".unipi", "docs", "plans");
66
- if (!existsSync(plansDir)) return [];
68
+ function suggestSpecFiles(prefix: string): CompletionItem[] {
69
+ return suggestFilesFrom("specs", "specs", "Spec", prefix);
70
+ }
67
71
 
68
- try {
69
- const search = prefix?.trim().split(/\s+/).pop() ?? "";
70
- const files = readdirSync(plansDir)
71
- .filter((f) => f.endsWith(".md"))
72
- .map((f) => ({ name: f, time: statSync(join(plansDir, f)).mtimeMs }))
73
- .sort((a, b) => b.time - a.time);
74
- return files
75
- .filter((f) => !search || f.name.includes(search))
76
- .map((f) => ({
77
- value: `plan:${f.name}`,
78
- label: basename(f.name, ".md"),
79
- description: `Plan: ${f.name}`,
80
- }));
81
- } catch {
82
- return [];
83
- }
72
+ function suggestPlanFiles(prefix: string): CompletionItem[] {
73
+ return suggestFilesFrom("plans", "plan", "Plan", prefix);
84
74
  }
85
75
 
86
- /**
87
- * Suggest debug files from .unipi/docs/debug/ for fix command.
88
- */
89
76
  function suggestDebugFiles(prefix: string): CompletionItem[] {
90
- const debugDir = join(process.cwd(), ".unipi", "docs", "debug");
91
- if (!existsSync(debugDir)) return [];
92
-
93
- try {
94
- const search = prefix?.trim().split(/\s+/).pop() ?? "";
95
- const files = readdirSync(debugDir)
96
- .filter((f) => f.endsWith(".md"))
97
- .map((f) => ({ name: f, time: statSync(join(debugDir, f)).mtimeMs }))
98
- .sort((a, b) => b.time - a.time);
99
- return files
100
- .filter((f) => !search || f.name.includes(search))
101
- .map((f) => ({
102
- value: `debug:${f.name}`,
103
- label: basename(f.name, ".md"),
104
- description: `Debug: ${f.name}`,
105
- }));
106
- } catch {
107
- return [];
108
- }
77
+ return suggestFilesFrom("debug", "debug", "Debug", prefix);
109
78
  }
110
79
 
111
- /**
112
- * Suggest chore files from .unipi/docs/chore/ for chore-execute command.
113
- */
114
80
  function suggestChoreFiles(prefix: string): CompletionItem[] {
115
- const choreDir = join(process.cwd(), ".unipi", "docs", "chore");
116
- if (!existsSync(choreDir)) return [];
117
-
118
- try {
119
- const search = prefix?.trim().split(/\s+/).pop() ?? "";
120
- const files = readdirSync(choreDir)
121
- .filter((f) => f.endsWith(".md"))
122
- .map((f) => ({ name: f, time: statSync(join(choreDir, f)).mtimeMs }))
123
- .sort((a, b) => b.time - a.time);
124
- return files
125
- .filter((f) => !search || f.name.includes(search))
126
- .map((f) => ({
127
- value: `chore:${f.name}`,
128
- label: basename(f.name, ".md"),
129
- description: `Chore: ${f.name}`,
130
- }));
131
- } catch {
132
- return [];
133
- }
81
+ return suggestFilesFrom("chore", "chore", "Chore", prefix);
134
82
  }
135
83
 
136
- /** Cached per-cwd worktree suggestions. Worktree autocomplete can be invoked on every
137
- * keystroke, so the recursive scan is intentionally paid only once per session/cwd.
138
- */
84
+ /** Cached per-cwd worktree suggestions. */
139
85
  let worktreeSuggestionsCache: { cwd: string; items: CompletionItem[] } | null = null;
140
86
 
141
87
  /**
142
88
  * Suggest existing worktree names for merge/list commands.
143
- * Recursively scans for actual git worktrees (directories containing .git files).
89
+ * Uses `git worktree list --porcelain` for a fast, native scan.
144
90
  */
145
91
  function suggestWorktrees(): CompletionItem[] {
146
92
  const cwd = process.cwd();
@@ -148,49 +94,26 @@ function suggestWorktrees(): CompletionItem[] {
148
94
  return worktreeSuggestionsCache.items;
149
95
  }
150
96
 
151
- const worktreesDir = join(cwd, ".unipi", "worktrees");
152
- if (!existsSync(worktreesDir)) {
153
- worktreeSuggestionsCache = { cwd, items: [] };
154
- return worktreeSuggestionsCache.items;
155
- }
156
-
157
97
  try {
98
+ const output = execFileSync("git", ["worktree", "list", "--porcelain"], {
99
+ encoding: "utf-8",
100
+ cwd,
101
+ stdio: ["pipe", "pipe", "pipe"],
102
+ });
158
103
  const results: CompletionItem[] = [];
159
-
160
- /**
161
- * Recursively find worktree directories (those containing a .git file).
162
- * A worktree's .git file contains: gitdir: /path/to/.git/worktrees/<branch>
163
- */
164
- function findWorktrees(dir: string, relativePath: string): void {
165
- const entries = readdirSync(dir, { withFileTypes: true });
166
- for (const entry of entries) {
167
- const fullPath = join(dir, entry.name);
168
- const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
169
-
170
- if (entry.name === ".git" && entry.isFile()) {
171
- // This is a worktree — extract branch name from gitdir path
172
- try {
173
- const gitContent = readFileSync(fullPath, "utf-8").trim();
174
- const match = gitContent.match(/gitdir:\s+.+?\/([^/]+)$/);
175
- const branchName = match?.[1] ?? entry.name;
176
- results.push({
177
- value: branchName,
178
- label: branchName,
179
- description: `Worktree: ${relPath}`,
180
- });
181
- } catch {
182
- // Can't read .git file — skip
183
- }
184
- } else if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
185
- // Recurse into subdirectories (but not into nested .git or node_modules)
186
- findWorktrees(fullPath, relPath);
187
- }
104
+ for (const line of output.split("\n")) {
105
+ if (line.startsWith("worktree ")) {
106
+ const path = line.slice("worktree ".length);
107
+ const branch = basename(path);
108
+ results.push({
109
+ value: branch,
110
+ label: branch,
111
+ description: `Worktree: ${path}`,
112
+ });
188
113
  }
189
114
  }
190
-
191
- findWorktrees(worktreesDir, "");
192
115
  worktreeSuggestionsCache = { cwd, items: results };
193
- return worktreeSuggestionsCache.items;
116
+ return results;
194
117
  } catch {
195
118
  worktreeSuggestionsCache = { cwd, items: [] };
196
119
  return worktreeSuggestionsCache.items;
@@ -482,10 +405,3 @@ export function registerWorkflowCommands(
482
405
  },
483
406
  });
484
407
  }
485
-
486
- /**
487
- * Get list of all registered workflow command names.
488
- */
489
- export function getWorkflowCommandNames(): string[] {
490
- return COMMANDS.map((c) => `${UNIPI_PREFIX}${c.name}`);
491
- }
@@ -23,6 +23,7 @@ import {
23
23
  getBlockedToolsForLevel,
24
24
  getSandboxLevel,
25
25
  isToolAllowed,
26
+ isActiveSnapshot,
26
27
  type SandboxLevel,
27
28
  } from "@pi-unipi/core";
28
29
  import { registerWorkflowCommands } from "./commands.js";
@@ -130,11 +131,6 @@ function latestHistoricalSandboxSnapshot(
130
131
  return undefined;
131
132
  }
132
133
 
133
- function isActiveSnapshot(snapshot: EffectiveSandboxSnapshot): boolean {
134
- if (typeof snapshot.details?.active === "boolean") return snapshot.details.active;
135
- return typeof snapshot.content === "string" && snapshot.content.includes("Status: active");
136
- }
137
-
138
134
  export default function (pi: ExtensionAPI) {
139
135
  const workflowLifecycle = new WorkflowLifecycle();
140
136
  let ralphDetected = false;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/workflow",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "Structured development workflow commands for Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -30,7 +30,7 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@pi-unipi/core": "2.6.0"
33
+ "@pi-unipi/core": "2.6.1"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@earendil-works/pi-ai": "^0.80.0",
@@ -1,94 +0,0 @@
1
- # @pi-unipi/cocoindex
2
-
3
- CocoIndex integration for Pi coding agent — AST-aware content indexing, semantic vector search, and incremental pipeline management.
4
-
5
- ## Overview
6
-
7
- Replaces the compactor's FTS5-based content indexing with [CocoIndex](https://cocoindex.io/), providing:
8
-
9
- - **AST-aware code chunking** — language-aware splitting for code files
10
- - **Semantic vector search** — find content by meaning, not just keywords
11
- - **Incremental indexing** — only reprocesses changed files (delta-only)
12
- - **LanceDB storage** — zero-config, local file-based vector database
13
- - **Shared embeddings** — reuses memory package's OpenRouter API key and model
14
-
15
- ## Prerequisites
16
-
17
- 1. **Python 3.10+**
18
- 2. **CocoIndex CLI**: `pip install cocoindex 'cocoindex[lancedb]'` (requires cocoindex >= 1.0)
19
- 3. **LanceDB SDK** (optional, for search): `npm install @lancedb/lancedb`
20
- 4. **Embedding API key** — configured via `/unipi:memory-settings`
21
-
22
- ## Quick Start
23
-
24
- ```
25
- # 1. Initialize the pipeline (once per project)
26
- /unipi:cocoindex-init
27
-
28
- # 2. Index the project
29
- /unipi:cocoindex-update
30
-
31
- # 3. Search indexed content
32
- cocoindex_search({ query: "how does authentication work?" })
33
- ```
34
-
35
- ## Architecture
36
-
37
- ```
38
- Project files ──→ localfs.walk_dir (recursive)
39
- │
40
- ▼
41
- chunk_text (@coco.fn, memoized)
42
- │
43
- ▼
44
- LanceDB target (via ContextKey)
45
- │
46
- ▼
47
- Vector search → ranked results
48
- ```
49
-
50
- Uses cocoindex v1.0+ App/fn/mount API with:
51
- - `@coco.lifespan` for async environment setup (LanceDB connection)
52
- - `@coco.fn` for memoized processing functions
53
- - `coco.mount()` / `coco.mount_target()` for component management
54
- - `localfs.walk_dir` for file enumeration
55
- - `lancedb.TableTarget` for row-level target state management
56
-
57
- ## Tools
58
-
59
- | Tool | Description |
60
- |------|-------------|
61
- | `cocoindex_search` | Search indexed content (semantic vector when available, LanceDB FTS when available, lexical fallback for text-only indexes); defaults to 10 results and hard-caps each page at 50 |
62
- | `cocoindex_status` | Check indexing status, freshness, doc count |
63
-
64
- ## Commands
65
-
66
- | Command | Description |
67
- |---------|-------------|
68
- | `/unipi:cocoindex-update` | Run incremental indexing |
69
- | `/unipi:cocoindex-status` | Show pipeline status |
70
- | `/unipi:cocoindex-init` | Scaffold default pipeline |
71
- | `/unipi:cocoindex-settings` | View configuration |
72
-
73
- ## Configuration
74
-
75
- - **Pipeline**: `.unipi/cocoindex/main.py` — auto-generated, fully customizable
76
- - **Data store**: `.unipi/cocoindex/.lancedb/`
77
- - **Embeddings**: `~/.unipi/memory/config.json` (shared with memory package)
78
- - **Search fallback**: Existing text-only LanceDB tables remain searchable through a lexical scan fallback when no vector column or FTS index exists
79
- - **Provider context bound**: Search pages are limited to 50 results, each rendered with a 300-character excerpt. Request another offset page instead of placing an unbounded result set into conversation history.
80
-
81
- ## What Changed from FTS5
82
-
83
- This package replaces compactor's content indexing subsystem:
84
-
85
- | Feature | Before (FTS5) | After (CocoIndex) |
86
- |---------|---------------|-------------------|
87
- | Chunking | Heading/paragraph | AST-aware recursive |
88
- | Search | BM25 + trigram | Vector + full-text |
89
- | Incremental | No (full re-index) | Yes (delta-only) |
90
- | Storage | SQLite FTS5 | LanceDB |
91
-
92
- ## Status
93
-
94
- ⚠️ **Experimental** — This is an `experiment/cocoindex` branch feature. Not yet merged to main.