Project Files
src / tools / git-clone-tool.ts
import { tool } from "@lmstudio/sdk"
import { simpleGit } from "simple-git"
import { z } from "zod"
import { formatGitError } from "../git/error-formatting"
import { resolveAndValidateRepoPath } from "../path/validation"
import type { Tool, ToolsProviderController } from "@lmstudio/sdk"
/**
* Create the git clone tool.
*
* @param ctl Tools provider controller supplied by the LM Studio SDK.
* @returns The configured git clone tool.
*/
export function createGitCloneTool(ctl: ToolsProviderController): Tool {
return tool({
name: "git_clone",
description:
"Clone a git repository to the local filesystem for discovery and inspection of repository content (tracked files, branches, commit history, etc.). Once a repository has been cloned, you can use the other git tools to inspect it.",
parameters: {
repoUrl: z.string().describe("The URL of the git repository to clone (e.g. https://github.com/user/repoName)."),
repoName: z
.string()
.optional()
.describe("Directory name for the cloned git repository. Defaults to the repository name."),
branch: z.string().optional().describe("Branch name to clone. Defaults to the repository's default branch."),
depth: z
.number()
.min(1)
.optional()
.describe("Number of commits to fetch. Use 1 for a shallow clone. Omit for a full clone."),
},
/**
* Executes the git clone command.
*
* @param arguments_ Validated tool parameters.
* @param arguments_.repoUrl URL of the git repository to clone.
* @param arguments_.repoName Optional directory name for the cloned repository inside the working directory.
* @param arguments_.branch Optional branch to clone; defaults to the repository's default branch.
* @param arguments_.depth Optional number of commits to fetch for a shallow clone.
* @param context Runtime tool context supplied by the SDK.
* @returns A confirmation string with the cloned repository name, or a user-facing error string.
*/
implementation: async (arguments_, context) => {
const { repoUrl, branch, depth } = arguments_
let { repoName } = arguments_
if (repoName === undefined) {
const inferredName = repoUrl.split("/").pop()?.replace(".git", "")
if (inferredName === undefined || inferredName === "" || inferredName === ".git") {
return `Error: git_clone could not derive a repository directory name from URL "${repoUrl}". Please supply repoName explicitly.`
}
repoName = inferredName
}
const destinationPath = resolveAndValidateRepoPath(repoName, ctl.getWorkingDirectory())
const cloneArguments: string[] = []
if (branch !== undefined) {
cloneArguments.push("--branch", branch)
}
if (depth !== undefined) {
cloneArguments.push("--depth", String(depth))
}
const branchSuffix = branch === undefined ? "" : ` (branch "${branch}")`
const depthSuffix = depth === undefined ? "" : ` with depth ${depth}`
context.status(`Cloning "${repoUrl}" into "${repoName}"${branchSuffix}${depthSuffix}…`)
try {
await simpleGit().clone(repoUrl, destinationPath, cloneArguments)
return `Cloned "${repoUrl}" to "${repoName}".`
} catch (error) {
return formatGitError("git_clone", repoUrl, error)
}
},
})
}