refactor(extension): auto-index on tool call and skip registration when binary missing
- Only register tool if `codexis` binary is in PATH - Run incremental index via `pi.exec` on each tool call to keep DB fresh - Remove `findDatabase` helper; derive DB path from git root directly - Replace `defineTool` with inline `pi.registerTool` call - Update imports (`@sinclair/typebox`, lazy `execSync`) - Fix output flag help text in main.go
This commit is contained in:
@@ -3,11 +3,14 @@
|
|||||||
*
|
*
|
||||||
* Provides a single tool that queries the .codexis/index.db SQLite database
|
* Provides a single tool that queries the .codexis/index.db SQLite database
|
||||||
* containing symbols, files, and line numbers for the codebase.
|
* containing symbols, files, and line numbers for the codebase.
|
||||||
|
*
|
||||||
|
* - Only registers if `codexis` binary is in PATH
|
||||||
|
* - Auto-indexes on first tool call if DB is missing
|
||||||
|
* - Re-indexes (incremental) on each call to keep index fresh
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Type } from "@mariozechner/pi-ai";
|
import { Type } from "@sinclair/typebox";
|
||||||
import { defineTool, type ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
import { execSync } from "node:child_process";
|
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import Database from "better-sqlite3";
|
import Database from "better-sqlite3";
|
||||||
@@ -56,6 +59,7 @@ Example queries:
|
|||||||
|
|
||||||
function findGitRoot(cwd: string): string | null {
|
function findGitRoot(cwd: string): string | null {
|
||||||
try {
|
try {
|
||||||
|
const { execSync } = require("node:child_process");
|
||||||
return execSync("git rev-parse --show-toplevel", {
|
return execSync("git rev-parse --show-toplevel", {
|
||||||
cwd,
|
cwd,
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
@@ -66,16 +70,23 @@ function findGitRoot(cwd: string): string | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function findDatabase(cwd: string): string | null {
|
function codexisAvailable(): boolean {
|
||||||
const gitRoot = findGitRoot(cwd);
|
try {
|
||||||
if (!gitRoot) return null;
|
const { execSync } = require("node:child_process");
|
||||||
const dbPath = join(gitRoot, ".codexis", "index.db");
|
execSync("which codexis", { stdio: ["pipe", "pipe", "pipe"] });
|
||||||
if (!existsSync(dbPath)) return null;
|
return true;
|
||||||
return dbPath;
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const codexisTool = defineTool({
|
export default function (pi: ExtensionAPI) {
|
||||||
name: "codexis",
|
if (!codexisAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pi.registerTool({
|
||||||
|
name: "Codexis",
|
||||||
label: "Codexis",
|
label: "Codexis",
|
||||||
description: DESCRIPTION,
|
description: DESCRIPTION,
|
||||||
parameters: Type.Object({
|
parameters: Type.Object({
|
||||||
@@ -84,11 +95,29 @@ const codexisTool = defineTool({
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
||||||
const dbPath = findDatabase(ctx.cwd);
|
const gitRoot = findGitRoot(ctx.cwd);
|
||||||
if (!dbPath) {
|
if (!gitRoot) {
|
||||||
|
throw new Error("Not in a git repository");
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbPath = join(gitRoot, ".codexis", "index.db");
|
||||||
|
|
||||||
|
// Run incremental index to keep DB fresh (fast if nothing changed)
|
||||||
|
const indexResult = await pi.exec("codexis", [gitRoot], {
|
||||||
|
signal,
|
||||||
|
timeout: 120_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (indexResult.code !== 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"No code index found. Run `codexis` in the repo root to generate .codexis/index.db"
|
`codexis indexing failed (exit ${indexResult.code}): ${indexResult.stderr}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existsSync(dbPath)) {
|
||||||
|
throw new Error(
|
||||||
|
"codexis ran but no index.db was produced. Check codexis output.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +131,9 @@ const codexisTool = defineTool({
|
|||||||
!normalized.startsWith("EXPLAIN") &&
|
!normalized.startsWith("EXPLAIN") &&
|
||||||
!normalized.startsWith("PRAGMA")
|
!normalized.startsWith("PRAGMA")
|
||||||
) {
|
) {
|
||||||
throw new Error("Only SELECT, WITH, EXPLAIN, and PRAGMA queries are allowed");
|
throw new Error(
|
||||||
|
"Only SELECT, WITH, EXPLAIN, and PRAGMA queries are allowed",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stmt = db.prepare(params.sql);
|
const stmt = db.prepare(params.sql);
|
||||||
@@ -123,7 +154,7 @@ const codexisTool = defineTool({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const widths = columns.map((col, i) =>
|
const widths = columns.map((col, i) =>
|
||||||
Math.max(col.length, ...data.map((row) => row[i].length))
|
Math.max(col.length, ...data.map((row) => row[i].length)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const header = columns
|
const header = columns
|
||||||
@@ -131,9 +162,7 @@ const codexisTool = defineTool({
|
|||||||
.join(" ");
|
.join(" ");
|
||||||
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
||||||
const body = data
|
const body = data
|
||||||
.map((row) =>
|
.map((row) => row.map((val, i) => val.padEnd(widths[i])).join(" "))
|
||||||
row.map((val, i) => val.padEnd(widths[i])).join(" ")
|
|
||||||
)
|
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
const result = `${header}\n${separator}\n${body}`;
|
const result = `${header}\n${separator}\n${body}`;
|
||||||
@@ -154,8 +183,5 @@ const codexisTool = defineTool({
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
|
||||||
pi.registerTool(codexisTool);
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -20,7 +20,7 @@ const dbFileName = "index.db"
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
force := flag.Bool("force", false, "Force full re-index (ignore file hashes)")
|
force := flag.Bool("force", false, "Force full re-index (ignore file hashes)")
|
||||||
output := flag.String("o", "", "Output database path (default: <root>/.codexis.db)")
|
output := flag.String("o", "", "Output database path (default: <root>/.codexis/index.db)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
root := "."
|
root := "."
|
||||||
|
|||||||
Reference in New Issue
Block a user