@astrofoundry/pi-astro 0.3.1 → 0.4.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/extensions/grimoire/index.ts +59 -17
- package/package.json +1 -1
|
@@ -3,18 +3,23 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
|
3
3
|
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
|
|
6
|
-
function loadGrimoireHelp(): string {
|
|
6
|
+
function loadGrimoireHelp(): { help: string; available: boolean } {
|
|
7
7
|
try {
|
|
8
|
-
|
|
8
|
+
const help = execFileSync("grimoire", ["--help"], {
|
|
9
9
|
encoding: "utf-8",
|
|
10
10
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11
11
|
}).trim();
|
|
12
|
+
return { help, available: true };
|
|
12
13
|
} catch {
|
|
13
|
-
return
|
|
14
|
+
return { help: "", available: false };
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const helpOutput = loadGrimoireHelp();
|
|
18
|
+
const { help: helpOutput, available: grimoireAvailable } = loadGrimoireHelp();
|
|
19
|
+
|
|
20
|
+
const NOT_INSTALLED_MESSAGE =
|
|
21
|
+
"grimoire CLI is not installed or not on PATH. Install it per your setup instructions " +
|
|
22
|
+
"(e.g. `npm install -g @astrofoundry/grimoire`), then restart pi so the extension can re-initialize.";
|
|
18
23
|
|
|
19
24
|
const params = Type.Object({
|
|
20
25
|
query: Type.String({ description: "Search query — use the library's own terminology." }),
|
|
@@ -30,17 +35,27 @@ export default function grimoireExtension(pi: ExtensionAPI): void {
|
|
|
30
35
|
label: "Grimoire",
|
|
31
36
|
description:
|
|
32
37
|
"Search indexed technical documentation via the grimoire CLI. Prefer this over web search for library/framework docs.",
|
|
33
|
-
promptGuidelines:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
promptGuidelines: grimoireAvailable
|
|
39
|
+
? [
|
|
40
|
+
"Use `grimoire` for ALL documentation lookups — libraries, frameworks, APIs, CLIs, cloud services.",
|
|
41
|
+
"Match query terminology to the library's own docs (e.g. 'Firestore pagination cursors' not 'how do I paginate').",
|
|
42
|
+
"If the first search misses, rephrase or scope with `source`.",
|
|
43
|
+
"Cite the URL from results when precision matters.",
|
|
44
|
+
"",
|
|
45
|
+
"Current `grimoire --help` output:",
|
|
46
|
+
helpOutput,
|
|
47
|
+
]
|
|
48
|
+
: [
|
|
49
|
+
`grimoire CLI was NOT available when pi started. ${NOT_INSTALLED_MESSAGE}`,
|
|
50
|
+
"Any call to this tool will return an error until grimoire is installed and pi is restarted.",
|
|
51
|
+
],
|
|
42
52
|
parameters: params,
|
|
43
|
-
async execute(_toolCallId, input, signal) {
|
|
53
|
+
async execute(_toolCallId, input, signal, _onUpdate, ctx) {
|
|
54
|
+
if (!grimoireAvailable) {
|
|
55
|
+
ctx.ui.notify("grimoire CLI not available — install it and restart pi.", "error");
|
|
56
|
+
throw new Error(NOT_INSTALLED_MESSAGE);
|
|
57
|
+
}
|
|
58
|
+
|
|
44
59
|
return new Promise<AgentToolResult<unknown>>((resolve, reject) => {
|
|
45
60
|
const args = ["search", input.query, "--compact"];
|
|
46
61
|
if (input.source) args.push("--source", input.source);
|
|
@@ -74,16 +89,43 @@ export default function grimoireExtension(pi: ExtensionAPI): void {
|
|
|
74
89
|
);
|
|
75
90
|
return;
|
|
76
91
|
}
|
|
77
|
-
|
|
92
|
+
|
|
93
|
+
const output = stdout.trim();
|
|
94
|
+
const isNoResults = output === "" || /^no results found\.?$/im.test(output);
|
|
95
|
+
|
|
96
|
+
if (isNoResults) {
|
|
97
|
+
const scopeNote = input.source ? ` in source '${input.source}'` : "";
|
|
98
|
+
const suggestion = input.source
|
|
99
|
+
? "Try rephrasing the query or widening scope by omitting `source`."
|
|
100
|
+
: "Try rephrasing with the library's own terminology, or check the indexed sources via `grimoire list --names`.";
|
|
101
|
+
ctx.ui.notify(`grimoire: no results for "${input.query}"${scopeNote}`, "warning");
|
|
102
|
+
resolve({
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: "text",
|
|
106
|
+
text: `No results found for "${input.query}"${scopeNote}. ${suggestion}`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
details: { query: input.query, source: input.source, results: 0 },
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
78
114
|
resolve({
|
|
79
|
-
content: [{ type: "text", text }],
|
|
115
|
+
content: [{ type: "text", text: output }],
|
|
80
116
|
details: { query: input.query, source: input.source },
|
|
81
117
|
});
|
|
82
118
|
});
|
|
83
119
|
|
|
84
120
|
proc.on("error", (err) => {
|
|
85
121
|
signal?.removeEventListener("abort", abortHandler);
|
|
86
|
-
|
|
122
|
+
const nodeErr = err as NodeJS.ErrnoException;
|
|
123
|
+
if (nodeErr.code === "ENOENT") {
|
|
124
|
+
ctx.ui.notify("grimoire CLI not available — install it and restart pi.", "error");
|
|
125
|
+
reject(new Error(NOT_INSTALLED_MESSAGE));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
reject(new Error(`grimoire CLI failed to run: ${err.message}`));
|
|
87
129
|
});
|
|
88
130
|
});
|
|
89
131
|
},
|