pi-byterover 0.2.6 → 0.2.7
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/package.json +2 -3
- package/src/byterover-lifecycle.ts +1 -16
- package/src/config-loader.ts +5 -6
- package/src/config.ts +40 -24
- package/src/gitignore.ts +2 -1
- package/src/recall.ts +1 -3
- package/src/tools.ts +19 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-byterover",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Pi extension that recalls and persists ByteRover memory.",
|
|
6
6
|
"keywords": [
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
"provenance": true
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@byterover/brv-bridge": "^1.2.0"
|
|
36
|
-
"zod": "^4.4.3"
|
|
35
|
+
"@byterover/brv-bridge": "^1.2.0"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
39
38
|
"byterover-cli": "3.16.1"
|
|
@@ -343,22 +343,7 @@ export const createByteRoverExtension = (
|
|
|
343
343
|
|
|
344
344
|
if (config.manualTools) {
|
|
345
345
|
registerManualTools({
|
|
346
|
-
pi
|
|
347
|
-
registerTool: (tool) => {
|
|
348
|
-
switch (tool.name) {
|
|
349
|
-
case "brv_recall":
|
|
350
|
-
pi.registerTool(tool);
|
|
351
|
-
return;
|
|
352
|
-
case "brv_search":
|
|
353
|
-
pi.registerTool(tool);
|
|
354
|
-
return;
|
|
355
|
-
case "brv_persist":
|
|
356
|
-
pi.registerTool(tool);
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
config,
|
|
346
|
+
pi,
|
|
362
347
|
bridge,
|
|
363
348
|
createBridge,
|
|
364
349
|
});
|
package/src/config-loader.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import type
|
|
5
|
-
import { ConfigSchema } from "./config.js";
|
|
4
|
+
import { parseConfigDocument, type ByteroverConfig } from "./config.js";
|
|
6
5
|
|
|
7
|
-
export type ByteroverConfig
|
|
6
|
+
export type { ByteroverConfig };
|
|
8
7
|
|
|
9
8
|
export type LoadConfigOptions = {
|
|
10
9
|
cwd: string;
|
|
@@ -21,7 +20,7 @@ const invalidConfig = (source: string, error: Error): LoadConfigResult => ({
|
|
|
21
20
|
error: new Error(`Invalid Byterover configuration in ${source}: ${error.message}`),
|
|
22
21
|
});
|
|
23
22
|
|
|
24
|
-
/** Loads the highest-precedence ByteRover JSON configuration through its
|
|
23
|
+
/** Loads the highest-precedence ByteRover JSON configuration through its TypeBox boundary. */
|
|
25
24
|
export const loadConfig = async ({
|
|
26
25
|
cwd,
|
|
27
26
|
homeDir = homedir(),
|
|
@@ -41,11 +40,11 @@ export const loadConfig = async ({
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
try {
|
|
44
|
-
return { success: true, source, config:
|
|
43
|
+
return { success: true, source, config: parseConfigDocument(JSON.parse(raw)) };
|
|
45
44
|
} catch (cause) {
|
|
46
45
|
return invalidConfig(source, cause instanceof Error ? cause : new Error(String(cause)));
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
return { success: true, config:
|
|
49
|
+
return { success: true, config: parseConfigDocument(undefined) };
|
|
51
50
|
};
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Decode, type StaticDecode, Type } from "typebox";
|
|
2
|
+
import { Value } from "typebox/value";
|
|
2
3
|
|
|
3
4
|
export const brvGitignoreBeginMarker = "# BEGIN pi-byterover";
|
|
4
5
|
export const brvGitignoreEndMarker = "# END pi-byterover";
|
|
@@ -46,29 +47,44 @@ export const configDefaults = {
|
|
|
46
47
|
maxRecallChars: 4096,
|
|
47
48
|
};
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
/** Raw, partially-specified Byterover configuration document. */
|
|
51
|
+
export type ByteroverConfigDocument = StaticDecode<typeof ConfigSchema>;
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
/** Fully defaulted Byterover configuration. */
|
|
54
|
+
export type ByteroverConfig = ByteroverConfigDocument & typeof configDefaults;
|
|
55
|
+
|
|
56
|
+
/** Parses one Byterover configuration document, rejecting invalid values, then applies defaults. */
|
|
57
|
+
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- This function IS the untrusted-document parser boundary.
|
|
58
|
+
export const parseConfigDocument = (value: unknown): ByteroverConfig => ({
|
|
59
|
+
...configDefaults,
|
|
60
|
+
...Value.Decode(ConfigSchema, value === undefined ? {} : value),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const trimmedNonEmptyString = () =>
|
|
64
|
+
Decode(Type.String({ minLength: 1, pattern: "\\S" }), (value) => value.trim());
|
|
65
|
+
|
|
66
|
+
export const ConfigSchema = Type.Object(
|
|
67
|
+
{
|
|
68
|
+
enabled: Type.Optional(Type.Boolean()),
|
|
55
69
|
// BrvBridge options
|
|
56
|
-
brvPath:
|
|
57
|
-
searchTimeoutMs:
|
|
58
|
-
recallTimeoutMs:
|
|
59
|
-
persistTimeoutMs:
|
|
70
|
+
brvPath: Type.Optional(trimmedNonEmptyString()),
|
|
71
|
+
searchTimeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
72
|
+
recallTimeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
73
|
+
persistTimeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
60
74
|
// Plugin options
|
|
61
|
-
quiet:
|
|
62
|
-
autoRecall:
|
|
63
|
-
autoPersist:
|
|
64
|
-
manualTools:
|
|
65
|
-
contextTagName:
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
quiet: Type.Optional(Type.Boolean()),
|
|
76
|
+
autoRecall: Type.Optional(Type.Boolean()),
|
|
77
|
+
autoPersist: Type.Optional(Type.Boolean()),
|
|
78
|
+
manualTools: Type.Optional(Type.Boolean()),
|
|
79
|
+
contextTagName: Type.Optional(
|
|
80
|
+
Decode(Type.String({ minLength: 1, pattern: "^\\s*[A-Za-z][A-Za-z0-9._-]*\\s*$" }), (value) =>
|
|
81
|
+
value.trim(),
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
recallPrompt: Type.Optional(trimmedNonEmptyString()),
|
|
85
|
+
persistPrompt: Type.Optional(trimmedNonEmptyString()),
|
|
86
|
+
maxRecallTurns: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
87
|
+
maxRecallChars: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
88
|
+
},
|
|
89
|
+
{ additionalProperties: false },
|
|
90
|
+
);
|
package/src/gitignore.ts
CHANGED
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
brvGitignoreRules,
|
|
8
8
|
} from "./config.js";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/** Escape one string for verbatim interpolation into a regular expression. */
|
|
11
|
+
export const escapeRegExp = (value: string) => {
|
|
11
12
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12
13
|
};
|
|
13
14
|
|
package/src/recall.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
-
};
|
|
1
|
+
import { escapeRegExp } from "./gitignore.js";
|
|
4
2
|
|
|
5
3
|
export const stripEchoedRecallQuery = (content: string, query: string) => {
|
|
6
4
|
const trimmedContent = content.trim();
|
package/src/tools.ts
CHANGED
|
@@ -6,15 +6,11 @@ import type {
|
|
|
6
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { type Static, type TSchema, Type } from "typebox";
|
|
8
8
|
import type { ByteRoverBridge, ByteRoverBridgeFactory } from "./byterover-bridge.js";
|
|
9
|
-
import type { ConfigSchema } from "./config.js";
|
|
10
9
|
import { stripEchoedRecallQuery } from "./recall.js";
|
|
11
10
|
|
|
12
|
-
type Config = ReturnType<typeof ConfigSchema.parse>;
|
|
13
|
-
|
|
14
11
|
export type RegisterManualToolsInput = {
|
|
15
12
|
pi: ByteRoverManualToolHost;
|
|
16
13
|
bridge: ByteRoverBridge;
|
|
17
|
-
config: Config;
|
|
18
14
|
createBridge: ByteRoverBridgeFactory;
|
|
19
15
|
};
|
|
20
16
|
|
|
@@ -114,9 +110,21 @@ export type ByteRoverManualToolDefinition =
|
|
|
114
110
|
| ManualToolDefinition<"brv_search", typeof SearchParameters>
|
|
115
111
|
| ManualToolDefinition<"brv_persist", typeof PersistParameters>;
|
|
116
112
|
|
|
117
|
-
/**
|
|
113
|
+
/** Lists the three registered manual-memory tool names. */
|
|
114
|
+
export const BYTE_ROVER_MANUAL_TOOL_NAMES = ["brv_recall", "brv_search", "brv_persist"] as const;
|
|
115
|
+
|
|
116
|
+
/** Narrows any tool definition to one of the three ByteRover manual tools by name. */
|
|
117
|
+
export const isByteRoverManualToolDefinition = (tool: {
|
|
118
|
+
readonly name: string;
|
|
119
|
+
}): tool is ByteRoverManualToolDefinition =>
|
|
120
|
+
// SAFETY: widening the const tuple to readonly string[] only relaxes literal checking for includes().
|
|
121
|
+
(BYTE_ROVER_MANUAL_TOOL_NAMES as readonly string[]).includes(tool.name);
|
|
122
|
+
|
|
123
|
+
/** Registers ByteRover manual tools with Pi or a faithful recording host. */
|
|
118
124
|
export interface ByteRoverManualToolHost {
|
|
119
|
-
registerTool
|
|
125
|
+
registerTool<TParams extends TSchema = TSchema, TDetails = unknown, TState = unknown>(
|
|
126
|
+
tool: ToolDefinition<TParams, TDetails, TState>,
|
|
127
|
+
): void;
|
|
120
128
|
}
|
|
121
129
|
|
|
122
130
|
const textResult = (text: string): AgentToolResult<undefined> => ({
|
|
@@ -124,8 +132,6 @@ const textResult = (text: string): AgentToolResult<undefined> => ({
|
|
|
124
132
|
details: undefined,
|
|
125
133
|
});
|
|
126
134
|
|
|
127
|
-
const errorMessage = (error: Error) => error.message;
|
|
128
|
-
|
|
129
135
|
export const formatSearchResults = (
|
|
130
136
|
results: readonly SearchResultItem[],
|
|
131
137
|
totalFound: number,
|
|
@@ -158,11 +164,9 @@ export const formatSearchResults = (
|
|
|
158
164
|
export const registerManualTools = ({
|
|
159
165
|
pi,
|
|
160
166
|
bridge,
|
|
161
|
-
config,
|
|
162
167
|
createBridge,
|
|
163
|
-
}: RegisterManualToolsInput) => {
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
}: Omit<RegisterManualToolsInput, "config">) => {
|
|
169
|
+
// ponytail: the session-start caller gates on config.manualTools before reaching this registration.
|
|
166
170
|
pi.registerTool({
|
|
167
171
|
name: "brv_recall",
|
|
168
172
|
label: "ByteRover Recall",
|
|
@@ -185,7 +189,7 @@ export const registerManualTools = ({
|
|
|
185
189
|
return textResult(content || "No relevant ByteRover context found.");
|
|
186
190
|
} catch (cause) {
|
|
187
191
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
188
|
-
return textResult(`ByteRover recall failed: ${
|
|
192
|
+
return textResult(`ByteRover recall failed: ${error.message}`);
|
|
189
193
|
}
|
|
190
194
|
},
|
|
191
195
|
});
|
|
@@ -214,7 +218,7 @@ export const registerManualTools = ({
|
|
|
214
218
|
);
|
|
215
219
|
} catch (cause) {
|
|
216
220
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
217
|
-
return textResult(`ByteRover search failed: ${
|
|
221
|
+
return textResult(`ByteRover search failed: ${error.message}`);
|
|
218
222
|
}
|
|
219
223
|
},
|
|
220
224
|
});
|
|
@@ -243,7 +247,7 @@ export const registerManualTools = ({
|
|
|
243
247
|
return textResult(`ByteRover persist ${brvResult.status}${suffix}`);
|
|
244
248
|
} catch (cause) {
|
|
245
249
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
246
|
-
return textResult(`ByteRover persist failed: ${
|
|
250
|
+
return textResult(`ByteRover persist failed: ${error.message}`);
|
|
247
251
|
}
|
|
248
252
|
},
|
|
249
253
|
});
|