@zj669/codeq-pi 0.3.1
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/README.md +38 -0
- package/package.json +49 -0
- package/src/extension.js +366 -0
- package/src/index.ts +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `@zj669/codeq-pi`
|
|
2
|
+
|
|
3
|
+
Native [Pi](https://github.com/earendil-works/pi) coding-agent **extension**
|
|
4
|
+
(not MCP). Same three tools as the `codeq` CLI: `find`, `grep`, `graph`.
|
|
5
|
+
Queries go to the existing user-level codeq daemon. Same-name `grep` / `find`
|
|
6
|
+
override Pi's builtin `rg` / `fd`; `graph` is new. Schema matches
|
|
7
|
+
`@zj669/codeq@0.3.1` (locators, literal grep, opaque grep cursor). This
|
|
8
|
+
directory is **not published to npm**; install from a checkout.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -g @zj669/codeq@0.3.1
|
|
12
|
+
pi install /absolute/path/to/agent_local_search/codeq-pi
|
|
13
|
+
# or, from a trusted project:
|
|
14
|
+
pi install -l /absolute/path/to/agent_local_search/codeq-pi
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Try once without writing settings:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pi -e /absolute/path/to/agent_local_search/codeq-pi/src/index.ts
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Needs **Node 22.5–24**, same as `@zj669/codeq`. Do not install alongside
|
|
24
|
+
`@ff-labs/pi-fff` in **override** mode — both register `grep` / `find`, and the
|
|
25
|
+
first loaded extension wins. Default pi-fff (`ffgrep` / `fffind`) does not
|
|
26
|
+
collide, but the model then sees two search stacks.
|
|
27
|
+
|
|
28
|
+
If you already pass `--tools`, include `grep,find,graph` or these tools never
|
|
29
|
+
enter the registry. `defaultTools` is not an install step.
|
|
30
|
+
|
|
31
|
+
Do **not** set `CODEQ_CWD`. Each call uses the Pi session `ctx.cwd`, plus
|
|
32
|
+
optional `path` / `root` (same meaning as CLI/MCP). One call, one repository.
|
|
33
|
+
Optional `CODEQ_JEV_KEY` on the Pi process reranks the current page the same
|
|
34
|
+
way CLI/MCP do; the reply never names that reranker.
|
|
35
|
+
|
|
36
|
+
There is no `detail` / `context` on these tools. `grep` is a literal by
|
|
37
|
+
default; pass `regex: true` for a regular expression and `cursor` to continue
|
|
38
|
+
the same search.
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zj669/codeq-pi",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Pi coding-agent extension: codeq find, grep, and graph (overrides builtin rg/fd)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"codeq",
|
|
10
|
+
"grep",
|
|
11
|
+
"find",
|
|
12
|
+
"graph"
|
|
13
|
+
],
|
|
14
|
+
"pi": {
|
|
15
|
+
"extensions": [
|
|
16
|
+
"./src/index.ts"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node --experimental-strip-types --disable-warning=ExperimentalWarning --test test/*.test.js"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.5 <25"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
31
|
+
"typebox": "*"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@zj669/codeq": "0.3.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typebox": "1.3.27"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/zj669/agent_local_search.git",
|
|
42
|
+
"directory": "codeq-pi"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/zj669/agent_local_search/tree/main/codeq-pi",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/zj669/agent_local_search/issues"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT"
|
|
49
|
+
}
|
package/src/extension.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Pi extension factory. Registers find / grep / graph with those names
|
|
3
|
+
* so they override Pi builtins (rg / fd). queryDaemon is injected by the
|
|
4
|
+
* default entry and must only run inside execute, never while registering.
|
|
5
|
+
*/
|
|
6
|
+
import { Type } from "typebox";
|
|
7
|
+
|
|
8
|
+
const PATH_DESCRIPTION =
|
|
9
|
+
"Narrow this call inside the selected root; never selects an index. Relative paths are relative to that root.";
|
|
10
|
+
|
|
11
|
+
const ROOT_DESCRIPTION =
|
|
12
|
+
"Repository/checkout/worktree for this call; overrides cwd/Git detection. Set it for cross-repo/worktree queries.";
|
|
13
|
+
|
|
14
|
+
const FIND_DESCRIPTION =
|
|
15
|
+
"codeq fuzzy file/path lookup, including dotfiles — not Pi's builtin fd. Query is a path fragment, not a glob.";
|
|
16
|
+
|
|
17
|
+
const GREP_DESCRIPTION =
|
|
18
|
+
"codeq literal string search by default; this is not rg and not Pi's builtin rg. Pass regex:true for a regular expression, fuzzy:true for approximate/different identifiers. Returns matching lines.";
|
|
19
|
+
|
|
20
|
+
const GRAPH_DESCRIPTION =
|
|
21
|
+
'codeq graph: identifiers or short "how does X work". Returns an entry span and direct callees, not an answer or source. Read the entry first; follow callees only as needed. Bounded neighborhood, not an exhaustive callgraph. There is no callers tool.';
|
|
22
|
+
|
|
23
|
+
function pathField() {
|
|
24
|
+
return Type.Optional(Type.String({ description: PATH_DESCRIPTION }));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function rootField() {
|
|
28
|
+
return Type.Optional(Type.String({ description: ROOT_DESCRIPTION }));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function limitField() {
|
|
32
|
+
return Type.Optional(
|
|
33
|
+
Type.Integer({
|
|
34
|
+
minimum: 1,
|
|
35
|
+
description: "Maximum number of matches to return",
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stripAt(value) {
|
|
41
|
+
if (typeof value !== "string") return value;
|
|
42
|
+
return value.startsWith("@") ? value.slice(1) : value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function trimString(value) {
|
|
46
|
+
return typeof value === "string" ? value.trim() : "";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function positiveInteger(value, name, allowZero = false) {
|
|
50
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
51
|
+
if (!Number.isSafeInteger(parsed) || (allowZero ? parsed < 0 : parsed < 1)) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`${name} must be ${allowZero ? "a non-negative" : "a positive"} integer`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function prepareShared(args) {
|
|
60
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return args;
|
|
61
|
+
const next = { ...args };
|
|
62
|
+
if (next.path !== undefined) next.path = stripAt(next.path);
|
|
63
|
+
if (next.root !== undefined) next.root = stripAt(next.root);
|
|
64
|
+
delete next.cwd;
|
|
65
|
+
delete next.ignoreCase;
|
|
66
|
+
delete next.literal;
|
|
67
|
+
delete next.detail;
|
|
68
|
+
delete next.context;
|
|
69
|
+
return next;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function prepareFindArgs(args) {
|
|
73
|
+
const next = prepareShared(args);
|
|
74
|
+
if (!next || typeof next !== "object") return next;
|
|
75
|
+
if (typeof next.query !== "string" && typeof next.pattern === "string") {
|
|
76
|
+
next.query = next.pattern;
|
|
77
|
+
}
|
|
78
|
+
delete next.pattern;
|
|
79
|
+
return next;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function prepareGrepArgs(args) {
|
|
83
|
+
const next = prepareShared(args);
|
|
84
|
+
if (!next || typeof next !== "object") return next;
|
|
85
|
+
if (typeof next.pattern !== "string" && typeof next.query === "string") {
|
|
86
|
+
next.pattern = next.query;
|
|
87
|
+
}
|
|
88
|
+
delete next.query;
|
|
89
|
+
return next;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function sessionCwd(ctx) {
|
|
93
|
+
const cwd = ctx?.cwd;
|
|
94
|
+
if (typeof cwd !== "string" || cwd.trim() === "") {
|
|
95
|
+
throw new Error("codeq tools require session ctx.cwd");
|
|
96
|
+
}
|
|
97
|
+
return cwd;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function toolRequest(name, params, cwd) {
|
|
101
|
+
if (name === "find") {
|
|
102
|
+
if (typeof params.query !== "string" || params.query.trim() === "") {
|
|
103
|
+
throw new Error("find requires query");
|
|
104
|
+
}
|
|
105
|
+
const request = {
|
|
106
|
+
command: "find",
|
|
107
|
+
cwd,
|
|
108
|
+
query: params.query,
|
|
109
|
+
};
|
|
110
|
+
if (params.path !== undefined) request.path = String(params.path);
|
|
111
|
+
if (params.root !== undefined) request.root = String(params.root);
|
|
112
|
+
if (params.limit !== undefined) {
|
|
113
|
+
request.limit = positiveInteger(params.limit, "limit");
|
|
114
|
+
}
|
|
115
|
+
return request;
|
|
116
|
+
}
|
|
117
|
+
if (name === "grep") {
|
|
118
|
+
if (typeof params.pattern !== "string" || params.pattern.trim() === "") {
|
|
119
|
+
throw new Error("grep requires pattern");
|
|
120
|
+
}
|
|
121
|
+
const request = {
|
|
122
|
+
command: "grep",
|
|
123
|
+
cwd,
|
|
124
|
+
query: params.pattern,
|
|
125
|
+
};
|
|
126
|
+
if (params.path !== undefined) request.path = String(params.path);
|
|
127
|
+
if (params.root !== undefined) request.root = String(params.root);
|
|
128
|
+
if (params.glob !== undefined) request.glob = String(params.glob);
|
|
129
|
+
if (params.fuzzy !== undefined) request.fuzzy = Boolean(params.fuzzy);
|
|
130
|
+
if (params.regex !== undefined) request.regex = Boolean(params.regex);
|
|
131
|
+
if (params.cursor !== undefined) request.cursor = String(params.cursor);
|
|
132
|
+
if (params.limit !== undefined) {
|
|
133
|
+
request.limit = positiveInteger(params.limit, "limit");
|
|
134
|
+
}
|
|
135
|
+
return request;
|
|
136
|
+
}
|
|
137
|
+
if (name === "graph") {
|
|
138
|
+
if (typeof params.query !== "string" || params.query.trim() === "") {
|
|
139
|
+
throw new Error("graph requires query");
|
|
140
|
+
}
|
|
141
|
+
const request = {
|
|
142
|
+
command: "graph",
|
|
143
|
+
cwd,
|
|
144
|
+
query: params.query,
|
|
145
|
+
};
|
|
146
|
+
if (params.path !== undefined) request.path = String(params.path);
|
|
147
|
+
if (params.root !== undefined) request.root = String(params.root);
|
|
148
|
+
return request;
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`unknown tool: ${name}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function paint(theme, color, text) {
|
|
154
|
+
return theme?.fg ? theme.fg(color, text) : text;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function textComponent(text) {
|
|
158
|
+
const lines = String(text).split("\n");
|
|
159
|
+
return {
|
|
160
|
+
render() {
|
|
161
|
+
return lines;
|
|
162
|
+
},
|
|
163
|
+
invalidate() {},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function renderCall(name, args, theme) {
|
|
168
|
+
const title = paint(theme, "toolTitle", theme?.bold ? theme.bold(name) : name);
|
|
169
|
+
const focus =
|
|
170
|
+
name === "grep"
|
|
171
|
+
? trimString(args?.pattern)
|
|
172
|
+
: trimString(args?.query) || trimString(args?.pattern);
|
|
173
|
+
const path = trimString(args?.path) || trimString(args?.root) || ".";
|
|
174
|
+
const body = focus
|
|
175
|
+
? `${title} ${paint(theme, "accent", focus)} ${paint(theme, "toolOutput", `in ${path}`)}`
|
|
176
|
+
: `${title} ${paint(theme, "toolOutput", path)}`;
|
|
177
|
+
return textComponent(body);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function renderResult(result, options, theme) {
|
|
181
|
+
const output =
|
|
182
|
+
result?.content?.find((part) => part.type === "text")?.text?.trim() ?? "";
|
|
183
|
+
if (!output) {
|
|
184
|
+
return textComponent(paint(theme, "muted", "No output"));
|
|
185
|
+
}
|
|
186
|
+
const color = result?.isError ? "error" : "toolOutput";
|
|
187
|
+
const lines = output.split("\n");
|
|
188
|
+
const shown = options?.expanded ? lines : lines.slice(0, 8);
|
|
189
|
+
const painted = shown.map((line) => paint(theme, color, line));
|
|
190
|
+
if (lines.length > shown.length) {
|
|
191
|
+
painted.push(
|
|
192
|
+
paint(theme, "muted", `... (${lines.length - shown.length} more lines)`),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return textComponent(painted.join("\n"));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function progressText(progress = {}) {
|
|
199
|
+
return (
|
|
200
|
+
progress.message ||
|
|
201
|
+
`${progress.phase || "indexing"}${
|
|
202
|
+
progress.total ? ` ${progress.current}/${progress.total}` : ""
|
|
203
|
+
}`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function createCodeqExtension({
|
|
208
|
+
query,
|
|
209
|
+
format,
|
|
210
|
+
rerank = async (_name, _request, result) => result,
|
|
211
|
+
} = {}) {
|
|
212
|
+
if (typeof query !== "function") {
|
|
213
|
+
throw new Error("createCodeqExtension requires query");
|
|
214
|
+
}
|
|
215
|
+
if (typeof format !== "function") {
|
|
216
|
+
throw new Error("createCodeqExtension requires format");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function executeTool(name) {
|
|
220
|
+
return async function execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
221
|
+
try {
|
|
222
|
+
if (signal?.aborted) {
|
|
223
|
+
throw new Error("request cancelled");
|
|
224
|
+
}
|
|
225
|
+
const cwd = sessionCwd(ctx);
|
|
226
|
+
const request = toolRequest(name, params ?? {}, cwd);
|
|
227
|
+
const result = await query(request, {
|
|
228
|
+
signal,
|
|
229
|
+
onProgress: (progress) => {
|
|
230
|
+
onUpdate?.({
|
|
231
|
+
content: [{ type: "text", text: progressText(progress) }],
|
|
232
|
+
details: {},
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
const ranked = await rerank(name, request, result);
|
|
237
|
+
const formatted = format(name, ranked);
|
|
238
|
+
return {
|
|
239
|
+
content: [{ type: "text", text: formatted.text }],
|
|
240
|
+
details: formatted.structuredContent ?? {},
|
|
241
|
+
};
|
|
242
|
+
} catch (error) {
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: error?.message || String(error),
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
details: {},
|
|
251
|
+
isError: true,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const tools = [
|
|
258
|
+
{
|
|
259
|
+
name: "find",
|
|
260
|
+
label: "find",
|
|
261
|
+
description: FIND_DESCRIPTION,
|
|
262
|
+
promptSnippet: "Find files by name or path (codeq / FFF, not fd)",
|
|
263
|
+
promptGuidelines: [
|
|
264
|
+
"find is codeq path search, not a glob tool and not fd: pass profile, not **/*profile*.",
|
|
265
|
+
"find matches the whole indexed path, including dotfiles such as .cursor/rules. Use grep for contents and graph for call chains.",
|
|
266
|
+
"find searches one repository per call. Pass root for another checkout; pass path to narrow inside the selected root. Never merge results across repositories.",
|
|
267
|
+
],
|
|
268
|
+
parameters: Type.Object({
|
|
269
|
+
query: Type.String({
|
|
270
|
+
description:
|
|
271
|
+
"Path fragment, matched fuzzily (foo.py, profiles/app, SKILL.md). Not a glob: **/*profile* matches nothing, pass profile. Dotfiles are indexed.",
|
|
272
|
+
}),
|
|
273
|
+
path: pathField(),
|
|
274
|
+
root: rootField(),
|
|
275
|
+
limit: limitField(),
|
|
276
|
+
}),
|
|
277
|
+
prepareArguments: prepareFindArgs,
|
|
278
|
+
execute: executeTool("find"),
|
|
279
|
+
renderCall(args, theme) {
|
|
280
|
+
return renderCall("find", args, theme);
|
|
281
|
+
},
|
|
282
|
+
renderResult,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "grep",
|
|
286
|
+
label: "grep",
|
|
287
|
+
description: GREP_DESCRIPTION,
|
|
288
|
+
promptSnippet: "Search file contents (codeq / FFF, not rg)",
|
|
289
|
+
promptGuidelines: [
|
|
290
|
+
"grep is codeq content search, not rg. Default matching is a literal string. Pass regex: true for a regular expression.",
|
|
291
|
+
"grep is exact by default: zero hits means zero hits. Pass fuzzy: true only for approximate/different identifiers; those replies are labelled [fuzzy].",
|
|
292
|
+
"grep searches one repository per call. Pass root for another checkout; pass path to narrow. Continuation uses an opaque cursor bound to the same search.",
|
|
293
|
+
],
|
|
294
|
+
parameters: Type.Object({
|
|
295
|
+
pattern: Type.String({
|
|
296
|
+
description:
|
|
297
|
+
"literal string by default. This is not rg: dots, brackets, and $ are literal unless regex is true.",
|
|
298
|
+
}),
|
|
299
|
+
path: pathField(),
|
|
300
|
+
root: rootField(),
|
|
301
|
+
glob: Type.Optional(
|
|
302
|
+
Type.String({
|
|
303
|
+
description:
|
|
304
|
+
"Optional glob used to constrain matches, for example **/*.ts",
|
|
305
|
+
}),
|
|
306
|
+
),
|
|
307
|
+
regex: Type.Optional(
|
|
308
|
+
Type.Boolean({
|
|
309
|
+
description:
|
|
310
|
+
"Default false. When true, pattern is a regular expression. Leave it off for a literal search — foo.ts, process.env, and array[0] are literals. This is not rg.",
|
|
311
|
+
}),
|
|
312
|
+
),
|
|
313
|
+
fuzzy: Type.Optional(
|
|
314
|
+
Type.Boolean({
|
|
315
|
+
description:
|
|
316
|
+
"Default false. Approximate matching for different identifiers, labelled [fuzzy]. NOT the same identifier.",
|
|
317
|
+
}),
|
|
318
|
+
),
|
|
319
|
+
cursor: Type.Optional(
|
|
320
|
+
Type.String({
|
|
321
|
+
description:
|
|
322
|
+
"Opaque continuation from a previous grep nextCursor. Bound to the same root, pattern, glob, path, regex, and fuzzy. A mismatch is an error, not page 1.",
|
|
323
|
+
}),
|
|
324
|
+
),
|
|
325
|
+
limit: limitField(),
|
|
326
|
+
}),
|
|
327
|
+
prepareArguments: prepareGrepArgs,
|
|
328
|
+
execute: executeTool("grep"),
|
|
329
|
+
renderCall(args, theme) {
|
|
330
|
+
return renderCall("grep", args, theme);
|
|
331
|
+
},
|
|
332
|
+
renderResult,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "graph",
|
|
336
|
+
label: "graph",
|
|
337
|
+
description: GRAPH_DESCRIPTION,
|
|
338
|
+
promptSnippet: "Explore the code graph (codeq / CodeGraph)",
|
|
339
|
+
promptGuidelines: [
|
|
340
|
+
'graph is codeq CodeGraph explore, not a written answer. Query identifiers or "how does X work" where X is identifiers — not a multi-paragraph question.',
|
|
341
|
+
"graph returns an entry span and direct callees. Read the entry first; follow callees only as needed. There is no callers tool.",
|
|
342
|
+
"graph searches one repository per call. Pass root for another checkout; pass path to narrow. Replies are locators, never source.",
|
|
343
|
+
],
|
|
344
|
+
parameters: Type.Object({
|
|
345
|
+
query: Type.String({
|
|
346
|
+
description:
|
|
347
|
+
'Identifiers, or "how does X work" where X is identifiers. Identifier-shaped queries match the graph; a multi-paragraph question does not.',
|
|
348
|
+
}),
|
|
349
|
+
path: pathField(),
|
|
350
|
+
root: rootField(),
|
|
351
|
+
}),
|
|
352
|
+
prepareArguments: prepareShared,
|
|
353
|
+
execute: executeTool("graph"),
|
|
354
|
+
renderCall(args, theme) {
|
|
355
|
+
return renderCall("graph", args, theme);
|
|
356
|
+
},
|
|
357
|
+
renderResult,
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
return function codeqPi(pi) {
|
|
362
|
+
for (const tool of tools) {
|
|
363
|
+
pi.registerTool(tool);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi coding-agent extension: same-name grep / find / graph over the codeq daemon.
|
|
3
|
+
* Do not start the daemon here — queryDaemon autostarts on first execute.
|
|
4
|
+
*/
|
|
5
|
+
import { queryDaemon } from "@zj669/codeq/src/client.js";
|
|
6
|
+
import { formatMcpToolResult } from "@zj669/codeq/src/mcp-format.js";
|
|
7
|
+
import { maybeRerank } from "@zj669/codeq/src/jev.js";
|
|
8
|
+
import { createCodeqExtension } from "./extension.js";
|
|
9
|
+
|
|
10
|
+
export { createCodeqExtension };
|
|
11
|
+
|
|
12
|
+
export default createCodeqExtension({
|
|
13
|
+
query: queryDaemon,
|
|
14
|
+
format: formatMcpToolResult,
|
|
15
|
+
rerank: maybeRerank,
|
|
16
|
+
});
|