From 733e2367252fd2ddafd12f8972a1de7117441646 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Thu, 5 Dec 2024 17:18:02 -0800 Subject: [PATCH] Remove cli directory (#41) --- cli/README.md | 110 ---------------- cli/api/mod.ts | 10 -- cli/api/providers/openrouter.ts | 147 --------------------- cli/core/StandaloneAgent.ts | 164 ----------------------- cli/core/prompts.ts | 120 ----------------- cli/deno.d.ts | 40 ------ cli/deno.jsonc | 13 -- cli/deno.lock | 87 ------------ cli/deps.ts | 21 --- cli/mod.ts | 123 ----------------- cli/tools/mod.ts | 225 -------------------------------- cli/types.d.ts | 43 ------ 12 files changed, 1103 deletions(-) delete mode 100644 cli/README.md delete mode 100644 cli/api/mod.ts delete mode 100644 cli/api/providers/openrouter.ts delete mode 100644 cli/core/StandaloneAgent.ts delete mode 100644 cli/core/prompts.ts delete mode 100644 cli/deno.d.ts delete mode 100644 cli/deno.jsonc delete mode 100644 cli/deno.lock delete mode 100644 cli/deps.ts delete mode 100644 cli/mod.ts delete mode 100644 cli/tools/mod.ts delete mode 100644 cli/types.d.ts diff --git a/cli/README.md b/cli/README.md deleted file mode 100644 index ceec8cc..0000000 --- a/cli/README.md +++ /dev/null @@ -1,110 +0,0 @@ -# Cline CLI - -A command-line interface for Cline, powered by Deno. - -## Installation - -1. Make sure you have [Deno](https://deno.land/) installed -2. Install the CLI globally: - ```bash - cd cli - deno task install - ``` - -If you get a PATH warning during installation, add Deno's bin directory to your PATH: -```bash -echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc # or ~/.zshrc -source ~/.bashrc # or ~/.zshrc -``` - -## Usage - -```bash -cline [options] -``` - -### Security Model - -The CLI implements several security measures: - -1. File Operations: - - Read/write access limited to working directory (--allow-read=., --allow-write=.) - - Prevents access to files outside the project - -2. Command Execution: - - Strict allowlist of safe commands: - * npm (install, run, test, build) - * git (status, add, commit, push, pull, clone, checkout, branch) - * deno (run, test, fmt, lint, check, compile, bundle) - * ls (-l, -a, -la, -lh) - * cat, echo - - Interactive prompts for non-allowlisted commands: - * y - Run once - * n - Cancel execution - * always - Remember for session - - Clear warnings and command details shown - - Session-based memory for approved commands - -3. Required Permissions: - - --allow-read=. - Read files in working directory - - --allow-write=. - Write files in working directory - - --allow-run - Execute allowlisted commands - - --allow-net - Make API calls - - --allow-env - Access environment variables - -### Options - -- `-m, --model ` - LLM model to use (default: "anthropic/claude-3.5-sonnet") -- `-k, --key ` - OpenRouter API key (required, or set OPENROUTER_API_KEY env var) -- `-h, --help` - Display help for command - -### Examples - -Analyze code: -```bash -export OPENROUTER_API_KEY=sk-or-v1-... -cline "Analyze this codebase" -``` - -Create files: -```bash -cline "Create a React component" -``` - -Run allowed command: -```bash -cline "Run npm install" -``` - -Run non-allowlisted command (will prompt for decision): -```bash -cline "Run yarn install" -# Responds with: -# Warning: Command not in allowlist -# Command: yarn install -# Do you want to run this command? (y/n/always) -``` - -## Development - -The CLI is built with Deno. Available tasks: - -```bash -# Run in development mode -deno task dev "your task here" - -# Install globally -deno task install - -# Type check the code -deno task check -``` - -### Security Features - -- File operations restricted to working directory -- Command execution controlled by allowlist -- Interactive prompts for unknown commands -- Session-based command approval -- Clear warnings and command details -- Permission validation at runtime diff --git a/cli/api/mod.ts b/cli/api/mod.ts deleted file mode 100644 index 8040c2c..0000000 --- a/cli/api/mod.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ApiConfiguration, ApiHandler } from "../types.d.ts"; -import { OpenRouterHandler } from "./providers/openrouter.ts"; - -// Re-export the ApiHandler interface -export type { ApiHandler }; - -export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { - const { apiKey, model } = configuration; - return new OpenRouterHandler({ apiKey, model }); -} diff --git a/cli/api/providers/openrouter.ts b/cli/api/providers/openrouter.ts deleted file mode 100644 index 9db32bb..0000000 --- a/cli/api/providers/openrouter.ts +++ /dev/null @@ -1,147 +0,0 @@ -import type { ApiStream, ModelInfo, Message, TextBlock } from "../../types.d.ts"; - -interface OpenRouterOptions { - model: string; - apiKey: string; -} - -export class OpenRouterHandler { - private apiKey: string; - private model: string; - - constructor(options: OpenRouterOptions) { - this.apiKey = options.apiKey; - this.model = options.model; - } - - async *createMessage(systemPrompt: string, messages: Message[]): ApiStream { - try { - // Convert our messages to OpenRouter format - const openRouterMessages = [ - { role: "system", content: systemPrompt }, - ...messages.map(msg => ({ - role: msg.role, - content: Array.isArray(msg.content) - ? msg.content.map(c => c.text).join("\n") - : msg.content - })) - ]; - - const response = await fetch("https://openrouter.ai/api/v1/chat/completions", { - method: "POST", - headers: { - "Authorization": `Bearer ${this.apiKey}`, - "Content-Type": "application/json", - "HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline", - "X-Title": "Roo Cline" - }, - body: JSON.stringify({ - model: this.model, - messages: openRouterMessages, - stream: true, - temperature: 0.7, - max_tokens: 4096 - }) - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => null); - throw new Error(`OpenRouter API error: ${response.statusText}${errorData ? ` - ${JSON.stringify(errorData)}` : ""}`); - } - - if (!response.body) { - throw new Error("No response body received"); - } - - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; - let content = ""; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - // Add new chunk to buffer and split into lines - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split("\n"); - - // Process all complete lines - buffer = lines.pop() || ""; // Keep the last incomplete line in buffer - - for (const line of lines) { - if (line.trim() === "") continue; - if (line === "data: [DONE]") continue; - - if (line.startsWith("data: ")) { - try { - const data = JSON.parse(line.slice(6)); - if (data.choices?.[0]?.delta?.content) { - const text = data.choices[0].delta.content; - content += text; - yield { type: "text", text }; - } - } catch (e) { - // Ignore parse errors for incomplete chunks - continue; - } - } - } - } - - // Process any remaining content in buffer - if (buffer.trim() && buffer.startsWith("data: ")) { - try { - const data = JSON.parse(buffer.slice(6)); - if (data.choices?.[0]?.delta?.content) { - const text = data.choices[0].delta.content; - content += text; - yield { type: "text", text }; - } - } catch (e) { - // Ignore parse errors for final incomplete chunk - } - } - - // Estimate token usage (4 chars per token is a rough estimate) - const inputText = systemPrompt + messages.reduce((acc, msg) => - acc + (typeof msg.content === "string" ? - msg.content : - msg.content.reduce((a, b) => a + b.text, "")), ""); - - const inputTokens = Math.ceil(inputText.length / 4); - const outputTokens = Math.ceil(content.length / 4); - - yield { - type: "usage", - inputTokens, - outputTokens, - totalCost: this.calculateCost(inputTokens, outputTokens) - }; - - } catch (error) { - console.error("Error in OpenRouter API call:", error); - throw error; - } - } - - getModel(): { id: string; info: ModelInfo } { - return { - id: this.model, - info: { - contextWindow: 128000, // This varies by model - supportsComputerUse: true, - inputPricePerToken: 0.000002, // Approximate, varies by model - outputPricePerToken: 0.000002 - } - }; - } - - private calculateCost(inputTokens: number, outputTokens: number): number { - const { inputPricePerToken, outputPricePerToken } = this.getModel().info; - return ( - (inputTokens * (inputPricePerToken || 0)) + - (outputTokens * (outputPricePerToken || 0)) - ); - } -} diff --git a/cli/core/StandaloneAgent.ts b/cli/core/StandaloneAgent.ts deleted file mode 100644 index da2abe5..0000000 --- a/cli/core/StandaloneAgent.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { blue, red, yellow } from "../deps.ts"; -import { ApiHandler } from "../api/mod.ts"; -import { executeCommand, readFile, writeFile, searchFiles, listFiles, listCodeDefinitions } from "../tools/mod.ts"; -import type { Message, TextBlock, ToolResult } from "../types.d.ts"; - -interface AgentConfig { - api: ApiHandler; - systemPrompt: string; - workingDir: string; -} - -export class StandaloneAgent { - private api: ApiHandler; - private systemPrompt: string; - private workingDir: string; - private conversationHistory: Message[] = []; - - constructor(config: AgentConfig) { - this.api = config.api; - this.systemPrompt = config.systemPrompt; - this.workingDir = config.workingDir; - } - - async runTask(task: string): Promise { - this.conversationHistory.push({ - role: "user", - content: [{ type: "text", text: `\n${task}\n` }] - }); - - let isTaskComplete = false; - const encoder = new TextEncoder(); - - while (!isTaskComplete) { - const stream = this.api.createMessage(this.systemPrompt, this.conversationHistory); - let assistantMessage = ""; - - console.log(blue("Thinking...")); - for await (const chunk of stream) { - if (chunk.type === "text") { - assistantMessage += chunk.text; - await Deno.stdout.write(encoder.encode(chunk.text)); - } - } - - this.conversationHistory.push({ - role: "assistant", - content: [{ type: "text", text: assistantMessage }] - }); - - const toolResults = await this.executeTools(assistantMessage); - - if (toolResults.length > 0) { - this.conversationHistory.push({ - role: "user", - content: toolResults.map(result => ({ - type: "text", - text: `[${result.tool}] Result:${result.output}` - })) as TextBlock[] - }); - } else { - if (assistantMessage.includes("")) { - isTaskComplete = true; - } else { - this.conversationHistory.push({ - role: "user", - content: [{ - type: "text", - text: "You must either use available tools to accomplish the task or call attempt_completion when the task is complete." - }] - }); - } - } - } - } - - private async executeTools(message: string): Promise { - const results: ToolResult[] = []; - const toolRegex = /<(\w+)>\s*([\s\S]*?)\s*<\/\1>/g; - let match; - - while ((match = toolRegex.exec(message)) !== null) { - const [_, toolName, paramsXml] = match; - const params: Record = {}; - const paramRegex = /<(\w+)>\s*([\s\S]*?)\s*<\/\1>/g; - let paramMatch; - - while ((paramMatch = paramRegex.exec(paramsXml)) !== null) { - const [__, paramName, paramValue] = paramMatch; - params[paramName] = paramValue.trim(); - } - - let output: string; - try { - console.log(yellow(`\nExecuting: ${this.getToolDescription(toolName, params)}`)); - - switch (toolName) { - case "execute_command": - output = await executeCommand(params.command); - break; - case "read_file": - output = await readFile(this.workingDir, params.path); - break; - case "write_to_file": - output = await writeFile(this.workingDir, params.path, params.content); - break; - case "search_files": - output = await searchFiles(this.workingDir, params.path, params.regex, params.file_pattern); - break; - case "list_files": - output = await listFiles(this.workingDir, params.path, params.recursive === "true"); - break; - case "list_code_definition_names": - output = await listCodeDefinitions(this.workingDir, params.path); - break; - case "attempt_completion": - return results; - default: - console.warn(red(`Unknown tool: ${toolName}`)); - continue; - } - - results.push({ - tool: toolName, - params, - output: output || "(No output)" - }); - - break; - } catch (error) { - const errorMessage = `Error executing ${toolName}: ${error instanceof Error ? error.message : String(error)}`; - console.error(red(errorMessage)); - results.push({ - tool: toolName, - params, - output: errorMessage - }); - break; - } - } - - return results; - } - - private getToolDescription(toolName: string, params: Record): string { - switch (toolName) { - case "execute_command": - return `Running command: ${params.command}`; - case "read_file": - return `Reading file: ${params.path}`; - case "write_to_file": - return `Writing to file: ${params.path}`; - case "search_files": - return `Searching for "${params.regex}" in ${params.path}`; - case "list_files": - return `Listing files in ${params.path}`; - case "list_code_definition_names": - return `Analyzing code in ${params.path}`; - case "attempt_completion": - return "Completing task"; - default: - return toolName; - } - } -} diff --git a/cli/core/prompts.ts b/cli/core/prompts.ts deleted file mode 100644 index 58653dd..0000000 --- a/cli/core/prompts.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { join } from "https://deno.land/std@0.220.1/path/mod.ts"; - -export const SYSTEM_PROMPT = async (cwd: string): Promise => { - let rulesContent = ""; - - // Load and combine rules from configuration files - const ruleFiles = ['.clinerules', '.cursorrules']; - for (const file of ruleFiles) { - const rulePath = join(cwd, file); - try { - const stat = await Deno.stat(rulePath); - if (stat.isFile) { - const content = await Deno.readTextFile(rulePath); - if (content.trim()) { - rulesContent += `\n# Rules from ${file}:\n${content.trim()}\n\n`; - } - } - } catch (err) { - // Only ignore ENOENT (file not found) errors - if (!(err instanceof Deno.errors.NotFound)) { - throw err; - } - } - } - - return `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices. - -==== - -TOOL USE - -You have access to tools that are executed upon approval. Use one tool per message and wait for the result before proceeding. Each tool must be used with proper XML-style formatting: - - -value1 -value2 - - -# Available Tools - -## execute_command -Description: Execute a CLI command on the system. Commands run in the current working directory: ${cwd} -Parameters: -- command: (required) The command to execute. Must be valid for the current OS. -Usage: - -command to run - - -## read_file -Description: Read contents of a file. Supports text files and automatically extracts content from PDFs/DOCXs. -Parameters: -- path: (required) Path to file (relative to ${cwd}) -Usage: - -path to file - - -## write_to_file -Description: Write content to a file. Creates directories as needed. Will overwrite existing files. -Parameters: -- path: (required) Path to write to (relative to ${cwd}) -- content: (required) Complete file content. Must include ALL parts, even unchanged sections. -Usage: - -path to file -complete file content - - -## search_files -Description: Search files using regex patterns. Shows matches with surrounding context. -Parameters: -- path: (required) Directory to search (relative to ${cwd}) -- regex: (required) Rust regex pattern to search for -- file_pattern: (optional) Glob pattern to filter files (e.g. "*.ts") -Usage: - -directory to search -pattern to search -optional file pattern - - -## list_code_definition_names -Description: List code definitions (classes, functions, etc.) in source files. -Parameters: -- path: (required) Directory to analyze (relative to ${cwd}) -Usage: - -directory to analyze - - -## attempt_completion -Description: Signal task completion and present results. -Parameters: -- result: (required) Description of completed work -- command: (optional) Command to demonstrate result -Usage: - -description of completed work -optional demo command - - -# Guidelines - -1. Use one tool at a time and wait for results -2. Provide complete file content when using write_to_file -3. Be direct and technical in responses -4. Present final results using attempt_completion -5. Do not make assumptions about command success -6. Do not make up commands that don't exist - -# Rules - -- Current working directory is: ${cwd} -- Cannot cd to different directories -- Must wait for confirmation after each tool use -- Must provide complete file content when writing files -- Be direct and technical, not conversational -- Do not end messages with questions${rulesContent}`; -}; diff --git a/cli/deno.d.ts b/cli/deno.d.ts deleted file mode 100644 index 61bf76d..0000000 --- a/cli/deno.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -declare namespace Deno { - export const args: string[]; - export function exit(code?: number): never; - export const env: { - get(key: string): string | undefined; - }; - export function cwd(): string; - export function readTextFile(path: string): Promise; - export function writeTextFile(path: string, data: string): Promise; - export function mkdir(path: string, options?: { recursive?: boolean }): Promise; - export function readDir(path: string): AsyncIterable<{ - name: string; - isFile: boolean; - isDirectory: boolean; - }>; - export function stat(path: string): Promise<{ - isFile: boolean; - isDirectory: boolean; - }>; - export class Command { - constructor(cmd: string, options?: { - args?: string[]; - stdout?: "piped"; - stderr?: "piped"; - }); - output(): Promise<{ - stdout: Uint8Array; - stderr: Uint8Array; - }>; - } - export const permissions: { - query(desc: { name: string; path?: string }): Promise<{ state: "granted" | "denied" }>; - }; - export const errors: { - PermissionDenied: typeof Error; - }; - export const stdout: { - write(data: Uint8Array): Promise; - }; -} diff --git a/cli/deno.jsonc b/cli/deno.jsonc deleted file mode 100644 index 8947075..0000000 --- a/cli/deno.jsonc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "strict": true, - "lib": ["deno.ns", "dom"] - }, - "tasks": { - "start": "deno run --allow-read=. mod.ts", - "dev": "deno run --allow-read=. mod.ts", - "install": "deno install --allow-read --allow-write --allow-net --allow-env --allow-run --global --name cline mod.ts", - "check": "deno check mod.ts" - } -} diff --git a/cli/deno.lock b/cli/deno.lock deleted file mode 100644 index e6d142f..0000000 --- a/cli/deno.lock +++ /dev/null @@ -1,87 +0,0 @@ -{ - "version": "4", - "remote": { - "https://deno.land/std@0.220.1/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5", - "https://deno.land/std@0.220.1/assert/assert_exists.ts": "24a7bf965e634f909242cd09fbaf38bde6b791128ece08e33ab08586a7cc55c9", - "https://deno.land/std@0.220.1/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", - "https://deno.land/std@0.220.1/flags/mod.ts": "9f13f3a49c54618277ac49195af934f1c7d235731bcf80fd33b8b234e6839ce9", - "https://deno.land/std@0.220.1/fmt/colors.ts": "d239d84620b921ea520125d778947881f62c50e78deef2657073840b8af9559a", - "https://deno.land/std@0.220.1/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8", - "https://deno.land/std@0.220.1/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2", - "https://deno.land/std@0.220.1/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c", - "https://deno.land/std@0.220.1/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c", - "https://deno.land/std@0.220.1/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", - "https://deno.land/std@0.220.1/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b", - "https://deno.land/std@0.220.1/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf", - "https://deno.land/std@0.220.1/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d", - "https://deno.land/std@0.220.1/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", - "https://deno.land/std@0.220.1/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3", - "https://deno.land/std@0.220.1/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607", - "https://deno.land/std@0.220.1/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a", - "https://deno.land/std@0.220.1/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883", - "https://deno.land/std@0.220.1/path/_interface.ts": "a1419fcf45c0ceb8acdccc94394e3e94f99e18cfd32d509aab514c8841799600", - "https://deno.land/std@0.220.1/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15", - "https://deno.land/std@0.220.1/path/basename.ts": "5d341aadb7ada266e2280561692c165771d071c98746fcb66da928870cd47668", - "https://deno.land/std@0.220.1/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643", - "https://deno.land/std@0.220.1/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36", - "https://deno.land/std@0.220.1/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c", - "https://deno.land/std@0.220.1/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441", - "https://deno.land/std@0.220.1/path/format.ts": "42a2f3201343df77061207e6aaf78c95bafce7f711dcb7fe1e5840311c505778", - "https://deno.land/std@0.220.1/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069", - "https://deno.land/std@0.220.1/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972", - "https://deno.land/std@0.220.1/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7", - "https://deno.land/std@0.220.1/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141", - "https://deno.land/std@0.220.1/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a", - "https://deno.land/std@0.220.1/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0", - "https://deno.land/std@0.220.1/path/mod.ts": "2821a1bb3a4148a0ffe79c92aa41aa9319fef73c6d6f5178f52b2c720d3eb02d", - "https://deno.land/std@0.220.1/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352", - "https://deno.land/std@0.220.1/path/normalize_glob.ts": "cc89a77a7d3b1d01053b9dcd59462b75482b11e9068ae6c754b5cf5d794b374f", - "https://deno.land/std@0.220.1/path/parse.ts": "65e8e285f1a63b714e19ef24b68f56e76934c3df0b6e65fd440d3991f4f8aefb", - "https://deno.land/std@0.220.1/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d", - "https://deno.land/std@0.220.1/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0", - "https://deno.land/std@0.220.1/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", - "https://deno.land/std@0.220.1/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1", - "https://deno.land/std@0.220.1/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00", - "https://deno.land/std@0.220.1/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2", - "https://deno.land/std@0.220.1/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1", - "https://deno.land/std@0.220.1/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40", - "https://deno.land/std@0.220.1/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f", - "https://deno.land/std@0.220.1/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede", - "https://deno.land/std@0.220.1/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", - "https://deno.land/std@0.220.1/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63", - "https://deno.land/std@0.220.1/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", - "https://deno.land/std@0.220.1/path/posix/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604", - "https://deno.land/std@0.220.1/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91", - "https://deno.land/std@0.220.1/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", - "https://deno.land/std@0.220.1/path/posix/parse.ts": "0b1fc4cb890dbb699ec1d2c232d274843b4a7142e1ad976b69fe51c954eb6080", - "https://deno.land/std@0.220.1/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c", - "https://deno.land/std@0.220.1/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf", - "https://deno.land/std@0.220.1/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf", - "https://deno.land/std@0.220.1/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0", - "https://deno.land/std@0.220.1/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add", - "https://deno.land/std@0.220.1/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d", - "https://deno.land/std@0.220.1/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b", - "https://deno.land/std@0.220.1/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40", - "https://deno.land/std@0.220.1/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808", - "https://deno.land/std@0.220.1/path/windows/basename.ts": "e2dbf31d1d6385bfab1ce38c333aa290b6d7ae9e0ecb8234a654e583cf22f8fe", - "https://deno.land/std@0.220.1/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", - "https://deno.land/std@0.220.1/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5", - "https://deno.land/std@0.220.1/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9", - "https://deno.land/std@0.220.1/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef", - "https://deno.land/std@0.220.1/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6", - "https://deno.land/std@0.220.1/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01", - "https://deno.land/std@0.220.1/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8", - "https://deno.land/std@0.220.1/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a", - "https://deno.land/std@0.220.1/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", - "https://deno.land/std@0.220.1/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf", - "https://deno.land/std@0.220.1/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", - "https://deno.land/std@0.220.1/path/windows/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604", - "https://deno.land/std@0.220.1/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780", - "https://deno.land/std@0.220.1/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", - "https://deno.land/std@0.220.1/path/windows/parse.ts": "dbdfe2bc6db482d755b5f63f7207cd019240fcac02ad2efa582adf67ff10553a", - "https://deno.land/std@0.220.1/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7", - "https://deno.land/std@0.220.1/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972", - "https://deno.land/std@0.220.1/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e", - "https://deno.land/std@0.220.1/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c" - } -} diff --git a/cli/deps.ts b/cli/deps.ts deleted file mode 100644 index fc8dc02..0000000 --- a/cli/deps.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Re-export standard library dependencies -export { parse } from "https://deno.land/std@0.220.1/flags/mod.ts"; -export { - blue, - red, - gray, - yellow, - bold, -} from "https://deno.land/std@0.220.1/fmt/colors.ts"; -export { - join, - dirname, -} from "https://deno.land/std@0.220.1/path/mod.ts"; - -// Export types -export type { - ApiHandler, - AgentConfig, - OperationMode, - ToolResponse, -} from "./types.d.ts"; diff --git a/cli/mod.ts b/cli/mod.ts deleted file mode 100644 index 5521cdd..0000000 --- a/cli/mod.ts +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env -S deno run --allow-read=. --allow-write=. --allow-run --allow-net --allow-env - -import { parse } from "./deps.ts"; -import { blue, red, gray, yellow, bold } from "./deps.ts"; -import { buildApiHandler } from "./api/mod.ts"; -import { StandaloneAgent } from "./core/StandaloneAgent.ts"; -import { SYSTEM_PROMPT } from "./core/prompts.ts"; -import type { ApiHandler, AgentConfig } from "./types.d.ts"; - -// Parse command line arguments -const args = parse(Deno.args, { - string: ["model", "key"], - boolean: ["help"], - alias: { - m: "model", - k: "key", - h: "help" - }, - default: { - model: "anthropic/claude-3.5-sonnet" - }, -}); - -if (args.help || Deno.args.length === 0) { - console.log(blue("\nCline - AI Coding Assistant\n")); - - console.log("Usage:"); - console.log(" cline [options]\n"); - - console.log("Required Permissions:"); - console.log(" --allow-read=. Read files in working directory"); - console.log(" --allow-write=. Write files in working directory"); - console.log(" --allow-run Execute commands (with interactive prompts)\n"); - console.log(" --allow-net Make API calls"); - console.log(" --allow-env Access environment variables\n"); - - console.log("Pre-approved Commands:"); - console.log(" npm - Package management (install, run, test, build)"); - console.log(" git - Version control (status, add, commit, push, pull, clone)"); - console.log(" deno - Deno runtime (run, test, fmt, lint, check)"); - console.log(" ls - List directory contents"); - console.log(" cat - Show file contents"); - console.log(" echo - Print text"); - console.log(" find - Search for files"); - console.log("\nOther commands will prompt for confirmation before execution.\n"); - - console.log("Options:"); - console.log(" -m, --model LLM model to use (default: \"anthropic/claude-3.5-sonnet\")"); - console.log(" -k, --key OpenRouter API key (or set OPENROUTER_API_KEY env var)"); - console.log(" -h, --help Display help for command\n"); - - console.log("Examples:"); - console.log(gray(" # Run pre-approved command")); - console.log(" cline \"Run npm install\"\n"); - - console.log(gray(" # Run command that requires confirmation")); - console.log(" cline \"Run yarn install\"\n"); - - Deno.exit(0); -} - -// Verify required permissions -const requiredPermissions = [ - { name: "read", path: "." }, - { name: "write", path: "." }, - { name: "run" }, - { name: "net" }, - { name: "env" } -] as const; - -for (const permission of requiredPermissions) { - const status = await Deno.permissions.query(permission); - if (status.state !== "granted") { - console.error(red(`Error: Missing required permission`)); - console.error(yellow(`Hint: Run with the following permissions:`)); - console.error(yellow(` deno run ${requiredPermissions.map(p => - "path" in p ? `--allow-${p.name}=${p.path}` : `--allow-${p.name}` - ).join(" ")} cli/mod.ts ...\n`)); - Deno.exit(1); - } -} - -const task = args._[0] as string; -const apiKey = args.key || Deno.env.get("OPENROUTER_API_KEY"); - -if (!apiKey) { - console.error(red("Error: OpenRouter API key is required. Set it with --key or OPENROUTER_API_KEY env var")); - console.error(yellow("Get your API key from: https://openrouter.ai/keys")); - Deno.exit(1); -} - -try { - const workingDir = Deno.cwd(); - - // Initialize API handler - const apiHandler = buildApiHandler({ - model: args.model, - apiKey - }); - - // Create agent instance - const agent = new StandaloneAgent({ - api: apiHandler, - systemPrompt: await SYSTEM_PROMPT(workingDir), - workingDir - }); - - // Run the task - console.log(blue(`\nStarting task: ${bold(task)}`)); - console.log(gray(`Working directory: ${workingDir}`)); - console.log(gray(`Model: ${args.model}`)); - console.log(gray("---\n")); - - await agent.runTask(task); - -} catch (error) { - if (error instanceof Error) { - console.error(red(`\nError: ${error.message}`)); - } else { - console.error(red("\nAn unknown error occurred")); - } - Deno.exit(1); -} diff --git a/cli/tools/mod.ts b/cli/tools/mod.ts deleted file mode 100644 index e2fbcd0..0000000 --- a/cli/tools/mod.ts +++ /dev/null @@ -1,225 +0,0 @@ -/// -import { join, dirname } from "https://deno.land/std@0.220.1/path/mod.ts"; -import { red, yellow, green } from "https://deno.land/std@0.220.1/fmt/colors.ts"; -import type { ToolResponse } from "../types.d.ts"; - -interface CommandConfig { - desc: string; - args: readonly string[]; -} - -// Define allowed commands and their descriptions -const ALLOWED_COMMANDS: Record = { - 'npm': { - desc: "Node package manager", - args: ["install", "run", "test", "build"] - }, - 'git': { - desc: "Version control", - args: ["status", "add", "commit", "push", "pull", "clone", "checkout", "branch"] - }, - 'deno': { - desc: "Deno runtime", - args: ["run", "test", "fmt", "lint", "check", "compile", "bundle"] - }, - 'ls': { - desc: "List directory contents", - args: ["-l", "-a", "-la", "-lh"] - }, - 'cat': { - desc: "Show file contents", - args: [] - }, - 'echo': { - desc: "Print text", - args: [] - } -}; - -// Track commands that have been allowed for this session -const alwaysAllowedCommands = new Set(); - -function isCommandAllowed(command: string): boolean { - // Split command into parts - const parts = command.trim().split(/\s+/); - if (parts.length === 0) return false; - - // Get base command - const baseCmd = parts[0]; - if (!(baseCmd in ALLOWED_COMMANDS)) return false; - - // If command has arguments, check if they're allowed - if (parts.length > 1 && ALLOWED_COMMANDS[baseCmd].args.length > 0) { - const arg = parts[1]; - return ALLOWED_COMMANDS[baseCmd].args.includes(arg); - } - - return true; -} - -async function promptForCommand(command: string): Promise { - // Check if command has been previously allowed - if (alwaysAllowedCommands.has(command)) { - console.log(yellow("\nWarning: Running previously allowed command:"), red(command)); - return true; - } - - console.log(yellow("\nWarning: Command not in allowlist")); - console.log("Command:", red(command)); - console.log("\nAllowed commands:"); - Object.entries(ALLOWED_COMMANDS).forEach(([cmd, { desc, args }]) => { - console.log(` ${green(cmd)}: ${desc}`); - if (args.length) { - console.log(` Arguments: ${args.join(", ")}`); - } - }); - - const answer = prompt("\nDo you want to run this command? (y/n/always) "); - if (answer?.toLowerCase() === 'always') { - alwaysAllowedCommands.add(command); - return true; - } - return answer?.toLowerCase() === 'y'; -} - -export async function executeCommand(command: string): Promise { - try { - // Check if command is allowed - if (!isCommandAllowed(command)) { - // Prompt user for confirmation - const shouldRun = await promptForCommand(command); - if (!shouldRun) { - return "Command execution cancelled by user"; - } - console.log(yellow("\nProceeding with command execution...")); - } - - const process = new Deno.Command("sh", { - args: ["-c", command], - stdout: "piped", - stderr: "piped", - }); - const { stdout, stderr } = await process.output(); - const decoder = new TextDecoder(); - return decoder.decode(stdout) + (stderr.length ? `\nStderr:\n${decoder.decode(stderr)}` : ""); - } catch (error) { - return `Error executing command: ${error instanceof Error ? error.message : String(error)}`; - } -} - -export async function readFile(workingDir: string, relativePath: string): Promise { - try { - const fullPath = join(workingDir, relativePath); - const content = await Deno.readTextFile(fullPath); - return content; - } catch (error) { - return `Error reading file: ${error instanceof Error ? error.message : String(error)}`; - } -} - -export async function writeFile(workingDir: string, relativePath: string, content: string): Promise { - try { - const fullPath = join(workingDir, relativePath); - await Deno.mkdir(dirname(fullPath), { recursive: true }); - await Deno.writeTextFile(fullPath, content); - return `Successfully wrote to ${relativePath}`; - } catch (error) { - return `Error writing file: ${error instanceof Error ? error.message : String(error)}`; - } -} - -export async function searchFiles( - workingDir: string, - searchPath: string, - regex: string, - filePattern?: string -): Promise { - try { - const fullPath = join(workingDir, searchPath); - const results: string[] = []; - - const regexObj = new RegExp(regex, "g"); - const patternObj = filePattern ? new RegExp(filePattern) : null; - - for await (const entry of Deno.readDir(fullPath)) { - if (entry.isFile && (!patternObj || patternObj.test(entry.name))) { - const filePath = join(fullPath, entry.name); - const content = await Deno.readTextFile(filePath); - const matches = content.match(regexObj); - if (matches) { - results.push(`File: ${entry.name}\nMatches:\n${matches.join("\n")}\n`); - } - } - } - - return results.join("\n") || "No matches found"; - } catch (error) { - return `Error searching files: ${error instanceof Error ? error.message : String(error)}`; - } -} - -export async function listFiles(workingDir: string, relativePath: string, recursive: boolean): Promise { - try { - const fullPath = join(workingDir, relativePath); - const files: string[] = []; - - async function* walkDir(dir: string): AsyncGenerator { - for await (const entry of Deno.readDir(dir)) { - const entryPath = join(dir, entry.name); - if (entry.isFile) { - yield entryPath.replace(fullPath + "/", ""); - } else if (recursive && entry.isDirectory) { - yield* walkDir(entryPath); - } - } - } - - for await (const file of walkDir(fullPath)) { - files.push(file); - } - - return files.join("\n") || "No files found"; - } catch (error) { - return `Error listing files: ${error instanceof Error ? error.message : String(error)}`; - } -} - -export async function listCodeDefinitions(workingDir: string, relativePath: string): Promise { - try { - const fullPath = join(workingDir, relativePath); - const content = await Deno.readTextFile(fullPath); - - // Basic regex patterns for common code definitions - const patterns = { - function: /(?:function|const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*(?:=\s*(?:function|\([^)]*\)\s*=>)|[({])/g, - class: /class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g, - method: /(?:async\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\([^)]*\)\s*{/g, - }; - - const definitions: Record = { - functions: [], - classes: [], - methods: [], - }; - - let match; - - while ((match = patterns.function.exec(content)) !== null) { - definitions.functions.push(match[1]); - } - - while ((match = patterns.class.exec(content)) !== null) { - definitions.classes.push(match[1]); - } - - while ((match = patterns.method.exec(content)) !== null) { - definitions.methods.push(match[1]); - } - - return Object.entries(definitions) - .map(([type, names]) => `${type}:\n${names.join("\n")}`) - .join("\n\n"); - } catch (error) { - return `Error listing code definitions: ${error instanceof Error ? error.message : String(error)}`; - } -} diff --git a/cli/types.d.ts b/cli/types.d.ts deleted file mode 100644 index ca81443..0000000 --- a/cli/types.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -export interface ApiHandler { - sendMessage(message: string): Promise; - createMessage(systemPrompt: string, history: Message[]): AsyncIterable; -} - -export interface AgentConfig { - api: ApiHandler; - systemPrompt: string; - workingDir: string; - debug?: boolean; -} - -export type ToolResponse = string; - -export interface Message { - role: "user" | "assistant"; - content: TextBlock[]; -} - -export interface TextBlock { - type: "text"; - text: string; -} - -export interface ToolResult { - tool: string; - params: Record; - output: string; -} - -export interface MessageChunk { - type: "text"; - text: string; -} - -export interface UsageBlock { - type: "usage"; - usage: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -}