@mrclrchtr/supi-prompt-suggestions 3.2.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@mrclrchtr/supi-core/package.json +2 -4
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +0 -4
- package/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +3 -2
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +0 -4
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +0 -10
- package/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +19 -15
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-action-menu.ts +1 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings.ts +1 -0
- package/package.json +2 -2
- package/src/generation/generator.ts +4 -7
- package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +0 -31
- package/node_modules/@mrclrchtr/supi-core/src/substrate-types.ts +0 -11
- package/node_modules/@mrclrchtr/supi-core/src/types.ts +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
},
|
|
51
51
|
"main": "src/api.ts",
|
|
52
52
|
"exports": {
|
|
53
|
-
"./abort-utils": "./src/abort-utils.ts",
|
|
54
53
|
"./api": "./src/api.ts",
|
|
55
54
|
"./config": "./src/config.ts",
|
|
56
55
|
"./context": "./src/context.ts",
|
|
@@ -71,7 +70,6 @@
|
|
|
71
70
|
"./status-spinner": "./src/status-spinner.ts",
|
|
72
71
|
"./terminal": "./src/terminal.ts",
|
|
73
72
|
"./tool-framework": "./src/tool-framework.ts",
|
|
74
|
-
"./prompt-surface": "./src/prompt-surface.ts"
|
|
75
|
-
"./types": "./src/types.ts"
|
|
73
|
+
"./prompt-surface": "./src/prompt-surface.ts"
|
|
76
74
|
}
|
|
77
75
|
}
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
// For lighter imports, use one of the domain subpaths directly
|
|
7
7
|
// (e.g. @mrclrchtr/supi-core/config, @mrclrchtr/supi-core/context).
|
|
8
8
|
|
|
9
|
-
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
10
|
-
export * from "./abort-utils.ts";
|
|
11
9
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
12
10
|
export * from "./config.ts";
|
|
13
11
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
@@ -42,5 +40,3 @@ export * from "./status-spinner.ts";
|
|
|
42
40
|
export * from "./terminal.ts";
|
|
43
41
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
44
42
|
export * from "./tool-framework.ts";
|
|
45
|
-
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
46
|
-
export * from "./types.ts";
|
|
@@ -22,14 +22,15 @@ export interface EvidenceBadgeInput {
|
|
|
22
22
|
* |------------------------------------------------------|---------------------------------------|
|
|
23
23
|
* | shown=12, total=12, omitted=0, label="references" | `12 references` |
|
|
24
24
|
* | shown=8, total=20, omitted=12, label="symbols" | `8 of 20 symbols (12 omitted)` |
|
|
25
|
-
* | shown=5,
|
|
25
|
+
* | shown=5, total=null, omitted=2, reason="timeout" | `5 matches (2 collected omitted; more may exist — timeout)` |
|
|
26
26
|
*/
|
|
27
27
|
export function formatEvidenceBadge(input: EvidenceBadgeInput): string {
|
|
28
28
|
const { shownCount, totalCount, omittedCount, partialReason, label } = input;
|
|
29
29
|
|
|
30
30
|
if (totalCount === null) {
|
|
31
31
|
const reasonSuffix = partialReason ? ` — ${partialReason}` : "";
|
|
32
|
-
|
|
32
|
+
const omittedPrefix = (omittedCount ?? 0) > 0 ? `${omittedCount} collected omitted; ` : "";
|
|
33
|
+
return `${shownCount} ${label} (${omittedPrefix}more may exist${reasonSuffix})`;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
if (omittedCount !== null && omittedCount > 0) {
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
// For lighter imports, use one of the domain subpaths directly
|
|
7
7
|
// (e.g. @mrclrchtr/supi-core/config, @mrclrchtr/supi-core/context).
|
|
8
8
|
|
|
9
|
-
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
10
|
-
export * from "./abort-utils.ts";
|
|
11
9
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
12
10
|
export * from "./config.ts";
|
|
13
11
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
@@ -36,5 +34,3 @@ export * from "./status-spinner.ts";
|
|
|
36
34
|
export * from "./terminal.ts";
|
|
37
35
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
38
36
|
export * from "./tool-framework.ts";
|
|
39
|
-
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
40
|
-
export * from "./types.ts";
|
|
@@ -24,16 +24,6 @@ export interface WithRetryOptions {
|
|
|
24
24
|
onRetry?: (attempt: number, delayMs: number) => void;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
* Attempt an async operation with retries and exponential backoff.
|
|
29
|
-
*
|
|
30
|
-
* If the signal is already aborted on entry, the operation is skipped entirely.
|
|
31
|
-
* If the signal aborts during a delay, the delay is cancelled immediately.
|
|
32
|
-
*
|
|
33
|
-
* @param fn - The async operation to retry.
|
|
34
|
-
* @param options - Optional configuration for retries, backoff, signal, and callbacks.
|
|
35
|
-
* @returns The result on success, or `null` if all attempts fail or the signal aborts.
|
|
36
|
-
*/
|
|
37
27
|
/**
|
|
38
28
|
* Create a promise that resolves after `ms` milliseconds, or rejects if
|
|
39
29
|
* the signal fires before the timeout elapses.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
3
|
|
|
3
4
|
/** Strip pi's optional leading `@` file-path prefix from a tool input. */
|
|
4
5
|
export function stripToolPathPrefix(target: string): string {
|
|
@@ -16,25 +17,28 @@ export function resolveToolPath(cwd: string, target: string): string {
|
|
|
16
17
|
return path.resolve(cwd, stripToolPathPrefix(target));
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Convert a file path to a file:// URI.
|
|
22
|
+
*
|
|
23
|
+
* Uses Node's `pathToFileURL` to produce a standards-compliant URI with
|
|
24
|
+
* proper percent-encoding of spaces, hashes, and other special characters.
|
|
25
|
+
*/
|
|
20
26
|
export function fileToUri(filePath: string): string {
|
|
21
|
-
|
|
22
|
-
if (process.platform === "win32") {
|
|
23
|
-
return `file:///${resolved.replace(/\\/g, "/")}`;
|
|
24
|
-
}
|
|
25
|
-
return `file://${resolved}`;
|
|
27
|
+
return pathToFileURL(filePath).href;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Convert a file:// URI to a file path.
|
|
32
|
+
*
|
|
33
|
+
* Uses Node's `fileURLToPath` for standards-compliant decoding. Non-file
|
|
34
|
+
* URIs are passed through unchanged so consumers (such as LSP diagnostic
|
|
35
|
+
* handling) remain compatible with non-file URI schemes.
|
|
36
|
+
*/
|
|
29
37
|
export function uriToFile(uri: string): string {
|
|
30
38
|
if (!uri.startsWith("file://")) return uri;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/^[A-Za-z]:/.test(filePath.slice(1))
|
|
36
|
-
) {
|
|
37
|
-
filePath = filePath.slice(1);
|
|
39
|
+
try {
|
|
40
|
+
return fileURLToPath(uri);
|
|
41
|
+
} catch {
|
|
42
|
+
return uri;
|
|
38
43
|
}
|
|
39
|
-
return filePath;
|
|
40
44
|
}
|
|
@@ -34,6 +34,7 @@ export function buildActionMenu(field: ScopedFieldValue, scope: SettingsScope):
|
|
|
34
34
|
if (choices.length > 0) {
|
|
35
35
|
for (const choice of choices) menu.push({ value: `set:${choice}`, label: choice });
|
|
36
36
|
} else if (field.field.kind === "number") menu.push({ value: "edit", label: "Edit value…" });
|
|
37
|
+
else if (field.field.kind === "string") menu.push({ value: "edit", label: "Edit value…" });
|
|
37
38
|
else if (field.field.kind === "stringList") menu.push({ value: "edit", label: "Edit values…" });
|
|
38
39
|
else if (field.field.kind === "modelPicker")
|
|
39
40
|
menu.push({ value: "edit", label: "Choose model…" });
|
|
@@ -86,6 +86,11 @@ export interface NumberField extends BaseField {
|
|
|
86
86
|
values?: string[];
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/** One free-form string. */
|
|
90
|
+
export interface StringField extends BaseField {
|
|
91
|
+
kind: "string";
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
/** Comma-separated string list. */
|
|
90
95
|
export interface StringListField extends BaseField {
|
|
91
96
|
kind: "stringList";
|
|
@@ -164,6 +169,7 @@ export type SettingsField =
|
|
|
164
169
|
| BoolField
|
|
165
170
|
| EnumField
|
|
166
171
|
| NumberField
|
|
172
|
+
| StringField
|
|
167
173
|
| StringListField
|
|
168
174
|
| ModelPickerField
|
|
169
175
|
| CustomField;
|
|
@@ -248,6 +254,8 @@ export function formatValue(value: unknown, field: SettingsField): string {
|
|
|
248
254
|
return value ? "on" : "off";
|
|
249
255
|
case "number":
|
|
250
256
|
return String(value ?? "");
|
|
257
|
+
case "string":
|
|
258
|
+
return typeof value === "string" && value ? value : "none";
|
|
251
259
|
case "stringList": {
|
|
252
260
|
const arr = Array.isArray(value) ? value : [];
|
|
253
261
|
return arr.length > 0 ? arr.map(String).join(", ") : "none";
|
|
@@ -271,6 +279,7 @@ export function sourceBadge(displayValue: string, source: ValueSource): string {
|
|
|
271
279
|
|
|
272
280
|
/** Format the value used to prefill editors and compare concrete choices. */
|
|
273
281
|
export function formatEditValue(value: unknown, field: SettingsField): string {
|
|
282
|
+
if (field.kind === "string") return typeof value === "string" ? value : "";
|
|
274
283
|
if (field.kind === "stringList") {
|
|
275
284
|
const arr = Array.isArray(value) ? value : [];
|
|
276
285
|
return arr.map(String).join(", ");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-prompt-suggestions",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Advisory ghost-text prompt suggestions for the pi coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@earendil-works/pi-ai": "*",
|
|
50
50
|
"@earendil-works/pi-coding-agent": "*",
|
|
51
51
|
"@earendil-works/pi-tui": "*",
|
|
52
|
-
"@mrclrchtr/supi-core": "
|
|
52
|
+
"@mrclrchtr/supi-core": "4.1.0"
|
|
53
53
|
},
|
|
54
54
|
"bundledDependencies": [
|
|
55
55
|
"@mrclrchtr/supi-core"
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { combineAbortSignals } from "@mrclrchtr/supi-core/abort-utils";
|
|
14
13
|
import { loadSectionConfig } from "@mrclrchtr/supi-core/config";
|
|
15
14
|
import { recordDebugEvent } from "@mrclrchtr/supi-core/debug";
|
|
16
15
|
import { CONFIG_SECTION, DEFAULTS } from "../config/config.ts";
|
|
@@ -176,10 +175,10 @@ export class SuggestionGenerator {
|
|
|
176
175
|
opts: RunOptions,
|
|
177
176
|
auth: ResolvedAuth,
|
|
178
177
|
): Promise<SuggestionClientOutput | null> {
|
|
179
|
-
const
|
|
180
|
-
opts.abort,
|
|
181
|
-
GENERATION_TIMEOUT_MS,
|
|
182
|
-
);
|
|
178
|
+
const combinedSignal = AbortSignal.any([
|
|
179
|
+
opts.abort.signal,
|
|
180
|
+
AbortSignal.timeout(GENERATION_TIMEOUT_MS),
|
|
181
|
+
]);
|
|
183
182
|
|
|
184
183
|
try {
|
|
185
184
|
return await callSuggestionModel({
|
|
@@ -201,8 +200,6 @@ export class SuggestionGenerator {
|
|
|
201
200
|
return null;
|
|
202
201
|
}
|
|
203
202
|
throw err;
|
|
204
|
-
} finally {
|
|
205
|
-
cleanupTimeout();
|
|
206
203
|
}
|
|
207
204
|
}
|
|
208
205
|
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Abort controller/signal utility helpers.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Combine a caller-provided abort controller with a generation timeout signal.
|
|
9
|
-
*
|
|
10
|
-
* Returns the combined signal and a cleanup function that removes both
|
|
11
|
-
* event listeners. Callers must invoke cleanup in a `finally` block
|
|
12
|
-
* to avoid listener leaks.
|
|
13
|
-
*/
|
|
14
|
-
export function combineAbortSignals(
|
|
15
|
-
abort: AbortController,
|
|
16
|
-
timeoutMs: number,
|
|
17
|
-
): { signal: AbortSignal; cleanup: () => void } {
|
|
18
|
-
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
19
|
-
const combinedController = new AbortController();
|
|
20
|
-
const onAbort = () => combinedController.abort();
|
|
21
|
-
abort.signal.addEventListener("abort", onAbort, { once: true });
|
|
22
|
-
timeoutSignal.addEventListener("abort", onAbort, { once: true });
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
signal: combinedController.signal,
|
|
26
|
-
cleanup: () => {
|
|
27
|
-
abort.signal.removeEventListener("abort", onAbort);
|
|
28
|
-
timeoutSignal.removeEventListener("abort", onAbort);
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/** 0-based position used by LSP and code-intelligence internally. */
|
|
2
|
-
export interface CodePosition {
|
|
3
|
-
line: number;
|
|
4
|
-
character: number;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/** Normalized location — flat replacement for LSP's nested Location/range shape. */
|
|
8
|
-
export interface CodeLocation {
|
|
9
|
-
uri: string;
|
|
10
|
-
range: { start: CodePosition; end: CodePosition };
|
|
11
|
-
}
|