import { z } from "zod";
import { NixOSClient } from "../nixos-client.ts";
import type { NixPackage } from "../types.ts";
/**
* Zod schema for search_nix_packages tool input validation
*/
export const searchPackagesInputSchema = z.object({
query: z.string().min(1, "Query cannot be empty").describe(
"Search query for Nix packages (e.g., 'python', 'vim', 'firefox')"
),
from: z
.number()
.int()
.min(0)
.default(0)
.describe("Starting index for pagination (default: 0)"),
size: z
.number()
.int()
.min(1)
.max(50)
.default(20)
.describe("Number of results to return (1-50, default: 20)"),
});
export type SearchPackagesInput = z.infer<typeof searchPackagesInputSchema>;
/**
* Format a single package for display
*/
function formatPackage(pkg: NixPackage, index: number): string {
const lines: string[] = [];
lines.push(`${index}. ${pkg.package_attr_name}`);
if (pkg.package_pversion) {
lines.push(` Version: ${pkg.package_pversion}`);
}
if (pkg.package_attr_set) {
lines.push(` Set: ${pkg.package_attr_set}`);
}
if (pkg.package_description) {
const wrapped = wrapText(pkg.package_description, 80);
lines.push(` Description: ${wrapped}`);
}
if (pkg.package_maintainers_set && pkg.package_maintainers_set.length > 0) {
const maintainers = pkg.package_maintainers_set.slice(0, 3).join(", ");
lines.push(` Maintainers: ${maintainers}${pkg.package_maintainers_set.length > 3 ? ", ..." : ""}`);
}
if (pkg.package_platforms && pkg.package_platforms.length > 0) {
const platforms = pkg.package_platforms.join(", ");
lines.push(` Platforms: ${platforms}`);
}
return lines.join("\n");
}
/**
* Wrap text to a specified width
*/
function wrapText(text: string, width: number): string {
const words = text.split(" ");
const lines: string[] = [];
let currentLine = "";
for (const word of words) {
if ((currentLine + word).length > width && currentLine.length > 0) {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = currentLine ? `${currentLine} ${word}` : word;
}
}
if (currentLine) {
lines.push(currentLine);
}
return lines.join("\n ");
}
/**
* Handler for the search_nix_packages tool
*/
export async function handleSearchPackages(
input: SearchPackagesInput
): Promise<string> {
const client = new NixOSClient();
try {
const { packages, total } = await client.searchPackages(
input.query,
input.from,
input.size
);
if (packages.length === 0) {
return `No Nix packages found for query: "${input.query}"`;
}
const resultLines: string[] = [];
resultLines.push(`Found ${total} results for "${input.query}":\n`);
packages.forEach((pkg, index) => {
resultLines.push(formatPackage(pkg, input.from + index + 1));
resultLines.push(""); // Empty line between results
});
const endIndex = input.from + packages.length;
resultLines.push("---");
resultLines.push(
`Showing results ${input.from + 1}-${endIndex} of ${total} total`
);
if (total > endIndex) {
resultLines.push(
`\nTo see more results, search again with from: ${endIndex}`
);
}
return resultLines.join("\n");
} catch (error) {
const message =
error instanceof Error ? error.message : "Unknown error occurred";
return `Error searching Nix packages: ${message}`;
}
}