@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.
- package/CHANGELOG.md +19 -0
- package/package.json +21 -22
- package/packages/ask-user/package.json +2 -2
- package/packages/ask-user/settings-tui.ts +18 -34
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -15
- package/packages/autocomplete/src/provider.ts +3 -17
- package/packages/btw/extensions/btw.ts +18 -45
- package/packages/btw/package.json +2 -2
- package/packages/compactor/README.md +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/compactor/skills/compactor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
- package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
- package/packages/compactor/src/commands/index.ts +47 -78
- package/packages/compactor/src/compaction/brief.ts +161 -90
- package/packages/compactor/src/compaction/build-sections.ts +3 -4
- package/packages/compactor/src/compaction/compact-args.ts +86 -0
- package/packages/compactor/src/compaction/cut.ts +270 -28
- package/packages/compactor/src/compaction/drill-down.ts +261 -0
- package/packages/compactor/src/compaction/format-recall.ts +96 -0
- package/packages/compactor/src/compaction/format.ts +8 -3
- package/packages/compactor/src/compaction/hooks.ts +248 -72
- package/packages/compactor/src/compaction/merge.ts +34 -4
- package/packages/compactor/src/compaction/rank.ts +270 -0
- package/packages/compactor/src/compaction/recall-scope.ts +28 -0
- package/packages/compactor/src/compaction/search-entries.ts +333 -96
- package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
- package/packages/compactor/src/compaction/summarize.ts +37 -6
- package/packages/compactor/src/compaction/token-estimate.ts +104 -0
- package/packages/compactor/src/compaction/touched-files.ts +35 -0
- package/packages/compactor/src/config/manager.ts +2 -27
- package/packages/compactor/src/config/presets.ts +0 -2
- package/packages/compactor/src/config/schema.ts +2 -16
- package/packages/compactor/src/executor/executor.ts +6 -15
- package/packages/compactor/src/executor/runtime.ts +2 -12
- package/packages/compactor/src/index.ts +4 -121
- package/packages/compactor/src/info-screen.ts +3 -10
- package/packages/compactor/src/security/evaluator.ts +0 -53
- package/packages/compactor/src/security/policy.ts +7 -8
- package/packages/compactor/src/session/db.ts +0 -6
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
- package/packages/compactor/src/tools/register.ts +27 -50
- package/packages/compactor/src/tools/vcc-recall.ts +86 -48
- package/packages/compactor/src/tui/settings-overlay.ts +20 -40
- package/packages/compactor/src/types.ts +43 -100
- package/packages/core/constants.ts +1 -28
- package/packages/core/events.ts +1 -83
- package/packages/core/index.ts +1 -0
- package/packages/core/model-cache.ts +18 -12
- package/packages/core/package.json +3 -1
- package/packages/core/tui-overlay.ts +89 -0
- package/packages/core/tui-width.ts +18 -0
- package/packages/core/utils.ts +43 -0
- package/packages/footer/package.json +2 -2
- package/packages/footer/src/help.ts +2 -2
- package/packages/footer/src/index.ts +9 -10
- package/packages/footer/src/presets.ts +1 -27
- package/packages/footer/src/registry/index.ts +0 -23
- package/packages/footer/src/rendering/icons.ts +0 -13
- package/packages/footer/src/rendering/renderer.ts +5 -40
- package/packages/footer/src/rendering/separators.ts +0 -10
- package/packages/footer/src/rendering/theme.ts +1 -15
- package/packages/footer/src/segments/compactor.ts +7 -14
- package/packages/footer/src/segments/core.ts +15 -54
- package/packages/footer/src/segments/kanboard.ts +5 -5
- package/packages/footer/src/segments/mcp.ts +5 -5
- package/packages/footer/src/segments/memory.ts +4 -4
- package/packages/footer/src/segments/notify.ts +2 -2
- package/packages/footer/src/segments/ralph.ts +3 -3
- package/packages/footer/src/segments/status-ext.ts +1 -1
- package/packages/footer/src/segments/workflow.ts +3 -3
- package/packages/footer/src/tps-tracker.ts +0 -8
- package/packages/footer/src/tui/settings-tui.ts +14 -31
- package/packages/footer/src/types.ts +0 -15
- package/packages/image/package.json +2 -2
- package/packages/image/src/register-providers.ts +0 -11
- package/packages/image/src/tools.ts +0 -3
- package/packages/image/src/tui/model-selector.ts +36 -71
- package/packages/info-screen/config.ts +0 -16
- package/packages/info-screen/core-groups.ts +0 -10
- package/packages/info-screen/index.ts +22 -13
- package/packages/info-screen/package.json +2 -2
- package/packages/info-screen/registry.ts +1 -72
- package/packages/info-screen/tui/info-overlay.ts +85 -100
- package/packages/info-screen/usage-parser.ts +12 -47
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/index.ts +3 -3
- package/packages/input-shortcuts/src/registers.ts +0 -8
- package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
- package/packages/input-shortcuts/src/undo-redo.ts +0 -12
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/package.json +2 -2
- package/packages/mcp/src/bridge/client.ts +0 -15
- package/packages/mcp/src/config/manager.ts +0 -38
- package/packages/mcp/src/config/schema.ts +0 -27
- package/packages/mcp/src/config/sync.ts +0 -29
- package/packages/mcp/src/index.ts +1 -1
- package/packages/mcp/src/tui/add-overlay.ts +6 -6
- package/packages/mcp/src/types.ts +0 -6
- package/packages/memory/embedding.ts +0 -31
- package/packages/memory/mempalace.ts +0 -16
- package/packages/memory/package.json +4 -8
- package/packages/memory/storage.ts +56 -753
- package/packages/milestone/hooks.ts +1 -6
- package/packages/milestone/milestone.ts +2 -41
- package/packages/milestone/package.json +2 -2
- package/packages/notify/README.md +1 -0
- package/packages/notify/commands.ts +61 -3
- package/packages/notify/events.ts +0 -10
- package/packages/notify/package.json +2 -2
- package/packages/notify/settings.ts +0 -19
- package/packages/notify/tui/gotify-setup.ts +108 -143
- package/packages/notify/tui/ntfy-setup.ts +137 -172
- package/packages/notify/tui/recap-model-selector.ts +107 -101
- package/packages/notify/tui/settings-overlay.ts +94 -117
- package/packages/notify/tui/telegram-setup.ts +48 -79
- package/packages/notify/types.ts +0 -2
- package/packages/ralph/index.ts +11 -35
- package/packages/ralph/package.json +3 -3
- package/packages/ralph/ralph-loop.ts +3 -44
- package/packages/subagents/package.json +2 -1
- package/packages/subagents/src/agent-manager.ts +0 -51
- package/packages/subagents/src/agent-runner.ts +6 -80
- package/packages/subagents/src/conversation-viewer.ts +1 -19
- package/packages/subagents/src/core-compat.ts +8 -25
- package/packages/subagents/src/custom-agents.ts +0 -1
- package/packages/subagents/src/index.ts +3 -48
- package/packages/subagents/src/types.ts +0 -11
- package/packages/subagents/src/widget.ts +5 -5
- package/packages/unipi/bundled.js +4468 -7769
- package/packages/unipi/index.ts +6 -3
- package/packages/updater/package.json +2 -2
- package/packages/updater/src/changelog.ts +1 -16
- package/packages/updater/src/index.ts +0 -5
- package/packages/updater/src/installer.ts +4 -26
- package/packages/updater/src/markdown.ts +6 -152
- package/packages/updater/src/settings.ts +0 -15
- package/packages/updater/src/tui/changelog-overlay.ts +42 -234
- package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
- package/packages/updater/src/tui/readme-overlay.ts +53 -210
- package/packages/updater/src/tui/settings-overlay.ts +13 -26
- package/packages/updater/src/version.ts +2 -31
- package/packages/utility/package.json +2 -4
- package/packages/utility/src/commands.ts +4 -5
- package/packages/utility/src/herdr-sync.ts +136 -0
- package/packages/utility/src/index.ts +11 -68
- package/packages/utility/src/lifecycle/cleanup.ts +2 -16
- package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
- package/packages/utility/src/tools/env.ts +0 -0
- package/packages/utility/src/tui/name-badge-state.ts +18 -2
- package/packages/utility/src/tui/name-badge.ts +5 -4
- package/packages/utility/src/tui/util-settings-tui.ts +41 -224
- package/packages/web-api/package.json +2 -6
- package/packages/web-api/src/engine/dependencies.ts +13 -134
- package/packages/web-api/src/engine/extract.ts +9 -99
- package/packages/web-api/src/engine/format.ts +0 -75
- package/packages/web-api/src/engine/types.ts +0 -39
- package/packages/web-api/src/index.ts +1 -2
- package/packages/web-api/src/providers/base.ts +0 -10
- package/packages/web-api/src/providers/duckduckgo.ts +0 -1
- package/packages/web-api/src/providers/firecrawl.ts +0 -9
- package/packages/web-api/src/providers/jina-reader.ts +0 -9
- package/packages/web-api/src/providers/jina-search.ts +0 -9
- package/packages/web-api/src/providers/perplexity.ts +0 -9
- package/packages/web-api/src/providers/registry.ts +0 -80
- package/packages/web-api/src/providers/serpapi.ts +0 -9
- package/packages/web-api/src/providers/tavily.ts +0 -9
- package/packages/web-api/src/providers/wigolo-client.ts +0 -30
- package/packages/web-api/src/providers/wigolo.ts +0 -1
- package/packages/web-api/src/settings.ts +17 -77
- package/packages/web-api/src/tools.ts +21 -24
- package/packages/web-api/src/tui/settings-dialog.ts +0 -1
- package/packages/workflow/commands.ts +38 -122
- package/packages/workflow/index.ts +1 -5
- package/packages/workflow/package.json +2 -2
- package/packages/cocoindex/README.md +0 -94
- package/packages/cocoindex/bridge.ts +0 -842
- package/packages/cocoindex/commands.ts +0 -175
- package/packages/cocoindex/index.ts +0 -48
- package/packages/cocoindex/installer.ts +0 -397
- package/packages/cocoindex/package.json +0 -49
- package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
- package/packages/cocoindex/tools.ts +0 -157
- package/packages/compactor/src/display/diff-renderer.ts +0 -281
- package/packages/compactor/src/display/line-width-safety.ts +0 -28
- package/packages/compactor/src/display/render-utils.ts +0 -52
- package/packages/compactor/src/display/thinking-label.ts +0 -18
- package/packages/compactor/src/display/tool-overrides.ts +0 -136
- package/packages/compactor/src/tools/compact.ts +0 -20
- package/packages/subagents/src/file-lock.ts +0 -102
- package/packages/utility/src/cache/ttl-cache.ts +0 -311
- package/packages/utility/src/diff/highlighter.ts +0 -352
- package/packages/utility/src/diff/parser.ts +0 -191
- package/packages/utility/src/diff/renderer.ts +0 -395
- package/packages/utility/src/diff/theme.ts +0 -316
- package/packages/utility/src/diff/wrapper.ts +0 -339
- package/packages/utility/src/display/capabilities.ts +0 -214
- package/packages/utility/src/display/width.ts +0 -226
- package/packages/utility/src/tools/batch.ts +0 -229
- package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
- package/packages/utility/src/tui/badge-settings.ts +0 -67
- package/packages/utility/src/tui/settings-inspector.ts +0 -303
- 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
|
}
|
|
@@ -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:
|
|
58
|
-
os:
|
|
59
|
-
maxChars:
|
|
60
|
-
timeoutMs:
|
|
61
|
-
batchConcurrency:
|
|
62
|
-
removeImages:
|
|
63
|
-
includeReplies:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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(
|
|
52
|
+
const files = readdirSync(dir)
|
|
46
53
|
.filter((f) => f.endsWith(".md"))
|
|
47
|
-
.map((f) => ({ name: f, time: statSync(join(
|
|
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:
|
|
59
|
+
value: `${valuePrefix}:${f.name}`,
|
|
53
60
|
label: basename(f.name, ".md"),
|
|
54
|
-
description:
|
|
61
|
+
description: `${descLabel}: ${f.name}`,
|
|
55
62
|
}));
|
|
56
63
|
} catch {
|
|
57
64
|
return [];
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
*
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|