summaryrefslogtreecommitdiff
path: root/packages/cli/src/utils/paths.ts
diff options
context:
space:
mode:
authorTaylor Mullen <[email protected]>2025-04-15 21:41:08 -0700
committerTaylor Mullen <[email protected]>2025-04-17 13:19:55 -0400
commitadd233c5043264d47ecc6d3339a383f41a241ae8 (patch)
tree3d80d412ed805007132cf44257bbd7667005dcd8 /packages/cli/src/utils/paths.ts
Initial commit of Gemini Code CLI
This commit introduces the initial codebase for the Gemini Code CLI, a command-line interface designed to facilitate interaction with the Gemini API for software engineering tasks. The code was migrated from a previous git repository as a single squashed commit. Core Features & Components: * **Gemini Integration:** Leverages the `@google/genai` SDK to interact with the Gemini models, supporting chat history, streaming responses, and function calling (tools). * **Terminal UI:** Built with Ink (React for CLIs) providing an interactive chat interface within the terminal, including input prompts, message display, loading indicators, and tool interaction elements. * **Tooling Framework:** Implements a robust tool system allowing Gemini to interact with the local environment. Includes tools for: * File system listing (`ls`) * File reading (`read-file`) * Content searching (`grep`) * File globbing (`glob`) * File editing (`edit`) * File writing (`write-file`) * Executing bash commands (`terminal`) * **State Management:** Handles the streaming state of Gemini responses and manages the conversation history. * **Configuration:** Parses command-line arguments (`yargs`) and loads environment variables (`dotenv`) for setup. * **Project Structure:** Organized into `core`, `ui`, `tools`, `config`, and `utils` directories using TypeScript. Includes basic build (`tsc`) and start scripts. This initial version establishes the foundation for a powerful CLI tool enabling developers to use Gemini for coding assistance directly in their terminal environment. --- Created by yours truly: __Gemini Code__
Diffstat (limited to 'packages/cli/src/utils/paths.ts')
-rw-r--r--packages/cli/src/utils/paths.ts102
1 files changed, 102 insertions, 0 deletions
diff --git a/packages/cli/src/utils/paths.ts b/packages/cli/src/utils/paths.ts
new file mode 100644
index 00000000..a5368463
--- /dev/null
+++ b/packages/cli/src/utils/paths.ts
@@ -0,0 +1,102 @@
+import process from 'node:process';
+import path from 'node:path'; // Import the 'path' module
+
+/**
+ * Returns the target directory, using the provided argument or the current working directory.
+ */
+export function getTargetDirectory(targetDirArg: string | undefined): string {
+ return targetDirArg || process.cwd();
+}
+
+/**
+ * Shortens a path string if it exceeds maxLen, prioritizing the start and end segments.
+ * Example: /path/to/a/very/long/file.txt -> /path/.../long/file.txt
+ */
+export function shortenPath(filePath: string, maxLen: number = 35): string {
+ if (filePath.length <= maxLen) {
+ return filePath;
+ }
+
+ const parsedPath = path.parse(filePath);
+ const root = parsedPath.root;
+ const separator = path.sep;
+
+ // Get segments of the path *after* the root
+ const relativePath = filePath.substring(root.length);
+ const segments = relativePath.split(separator).filter(s => s !== ''); // Filter out empty segments
+
+ // Handle cases with no segments after root (e.g., "/", "C:\") or only one segment
+ if (segments.length <= 1) {
+ // Fallback to simple start/end truncation for very short paths or single segments
+ const keepLen = Math.floor((maxLen - 3) / 2);
+ // Ensure keepLen is not negative if maxLen is very small
+ if (keepLen <= 0) {
+ return filePath.substring(0, maxLen - 3) + '...';
+ }
+ const start = filePath.substring(0, keepLen);
+ const end = filePath.substring(filePath.length - keepLen);
+ return `${start}...${end}`;
+ }
+
+ const firstDir = segments[0];
+ const startComponent = root + firstDir;
+
+ const endPartSegments: string[] = [];
+ // Base length: startComponent + separator + "..."
+ let currentLength = startComponent.length + separator.length + 3;
+
+ // Iterate backwards through segments (excluding the first one)
+ for (let i = segments.length - 1; i >= 1; i--) {
+ const segment = segments[i];
+ // Length needed if we add this segment: current + separator + segment
+ const lengthWithSegment = currentLength + separator.length + segment.length;
+
+ if (lengthWithSegment <= maxLen) {
+ endPartSegments.unshift(segment); // Add to the beginning of the end part
+ currentLength = lengthWithSegment;
+ } else {
+ // Adding this segment would exceed maxLen
+ break;
+ }
+ }
+
+ // Construct the final path
+ let result = startComponent + separator + '...';
+ if (endPartSegments.length > 0) {
+ result += separator + endPartSegments.join(separator);
+ }
+
+ // As a final check, if the result is somehow still too long (e.g., startComponent + ... is too long)
+ // fallback to simple truncation of the original path
+ if (result.length > maxLen) {
+ const keepLen = Math.floor((maxLen - 3) / 2);
+ if (keepLen <= 0) {
+ return filePath.substring(0, maxLen - 3) + '...';
+ }
+ const start = filePath.substring(0, keepLen);
+ const end = filePath.substring(filePath.length - keepLen);
+ return `${start}...${end}`;
+ }
+
+
+ return result;
+}
+
+/**
+ * Calculates the relative path from a root directory to a target path.
+ * Ensures both paths are resolved before calculating.
+ * Returns '.' if the target path is the same as the root directory.
+ *
+ * @param targetPath The absolute or relative path to make relative.
+ * @param rootDirectory The absolute path of the directory to make the target path relative to.
+ * @returns The relative path from rootDirectory to targetPath.
+ */
+export function makeRelative(targetPath: string, rootDirectory: string): string {
+ const resolvedTargetPath = path.resolve(targetPath);
+ const resolvedRootDirectory = path.resolve(rootDirectory);
+
+ const relativePath = path.relative(resolvedRootDirectory, resolvedTargetPath);
+
+ // If the paths are the same, path.relative returns '', return '.' instead
+ return relativePath || '.';
+}