multica/apps/cli/src/commands/tools.ts

175 lines
4.6 KiB
TypeScript

/**
* Tools command - Inspect and test tool policies
*
* Usage:
* multica tools list [options] List available tools
* multica tools groups Show all tool groups
*/
import { createAllTools } from "@multica/core";
import { filterTools, type ToolsConfig } from "@multica/core";
import { TOOL_GROUPS, expandToolGroups } from "@multica/core";
import { cyan, yellow, green, dim } from "../colors.js";
type Command = "list" | "groups" | "help";
interface ToolsOptions {
command: Command;
allow?: string[];
deny?: string[];
provider?: string | undefined;
isSubagent?: boolean;
}
function printHelp() {
console.log(`
${cyan("Usage:")} multica tools <command> [options]
${cyan("Commands:")}
${yellow("list")} List available tools (with optional filtering)
${yellow("groups")} Show all tool groups
${yellow("help")} Show this help
${cyan("Options for 'list':")}
${yellow("--allow")} TOOLS Allow specific tools (comma-separated)
${yellow("--deny")} TOOLS Deny specific tools (comma-separated)
${yellow("--provider")} NAME Apply provider-specific rules
${yellow("--subagent")} Apply subagent restrictions
${cyan("Examples:")}
${dim("# List all tools")}
multica tools list
${dim("# List tools with allow/deny")}
multica tools list --deny exec
multica tools list --allow group:fs,web_fetch
${dim("# Show tool groups")}
multica tools groups
`);
}
function parseArgs(argv: string[]): ToolsOptions {
const args = [...argv];
const raw = args.shift() || "help";
if (raw === "--help" || raw === "-h") {
return { command: "help" };
}
const command = raw as Command;
const opts: ToolsOptions = { command };
while (args.length > 0) {
const arg = args.shift();
if (!arg) break;
if (arg === "--help" || arg === "-h") {
return { command: "help" };
}
if (arg === "--allow") {
const value = args.shift();
opts.allow = value?.split(",").map((s) => s.trim()) ?? [];
continue;
}
if (arg === "--deny") {
const value = args.shift();
opts.deny = value?.split(",").map((s) => s.trim()) ?? [];
continue;
}
if (arg === "--provider") {
opts.provider = args.shift();
continue;
}
if (arg === "--subagent") {
opts.isSubagent = true;
continue;
}
}
return opts;
}
function cmdList(opts: ToolsOptions) {
const allTools = createAllTools(process.cwd());
console.log(`\n${cyan("Tools Overview")}`);
console.log(`Total tools available: ${allTools.length}\n`);
// Build config
let config: ToolsConfig | undefined;
if (opts.allow || opts.deny) {
config = {};
if (opts.allow) {
config.allow = opts.allow;
}
if (opts.deny) {
config.deny = opts.deny;
}
}
const filterOpts: import("@multica/core").FilterToolsOptions = {};
if (config) {
filterOpts.config = config;
}
if (opts.provider) {
filterOpts.provider = opts.provider;
}
if (opts.isSubagent) {
filterOpts.isSubagent = opts.isSubagent;
}
const filtered = filterTools(allTools, filterOpts);
if (config || opts.provider || opts.isSubagent) {
console.log("Applied filters:");
if (opts.allow) console.log(` ${dim("Allow:")} ${opts.allow.join(", ")}`);
if (opts.deny) console.log(` ${dim("Deny:")} ${opts.deny.join(", ")}`);
if (opts.provider) console.log(` ${dim("Provider:")} ${opts.provider}`);
if (opts.isSubagent) console.log(` ${dim("Subagent:")} true`);
console.log("");
console.log(`Tools after filtering: ${green(String(filtered.length))}`);
console.log("");
}
console.log("Tools:");
for (const tool of filtered) {
const desc = tool.description?.slice(0, 55) || "";
console.log(` ${yellow(tool.name.padEnd(15))} ${dim(desc)}${desc.length >= 55 ? "..." : ""}`);
}
if (filtered.length < allTools.length) {
const removed = allTools.filter((t) => !filtered.find((f) => f.name === t.name));
console.log("");
console.log(`${dim(`Filtered out (${removed.length}):`)}`);
for (const tool of removed) {
console.log(` ${dim(tool.name)}`);
}
}
}
function cmdGroups() {
console.log(`\n${cyan("Tool Groups:")}\n`);
for (const [name, tools] of Object.entries(TOOL_GROUPS)) {
console.log(` ${yellow(name)}:`);
console.log(` ${dim(tools.join(", "))}`);
console.log("");
}
}
export async function toolsCommand(args: string[]): Promise<void> {
const opts = parseArgs(args);
switch (opts.command) {
case "list":
cmdList(opts);
break;
case "groups":
cmdGroups();
break;
case "help":
default:
printHelp();
break;
}
}