Compare commits

...

9 Commits

Author SHA1 Message Date
Arafatkatze 39515dad02 Fix serilationaz 2025-10-11 16:58:55 -07:00
Arafatkatze fa45091f7f Fix serilationaz 2025-10-11 16:03:56 -07:00
Arafatkatze bdc710fcc0 Adding local install script 2025-10-11 15:23:03 -07:00
Arafatkatze 15233c84a6 Adding Redirect URL for CLI 2025-10-11 12:44:12 -07:00
Arafatkatze 30e1c4f0f9 debug logs added 2025-10-11 12:44:12 -07:00
Arafatkatze 22752c901e debug logs added 2025-10-11 12:44:12 -07:00
Arafatkatze ea4a76f95f refactor 2025-10-11 12:44:12 -07:00
Arafatkatze ae9db631df Temporary commit for workspace stuff 2025-10-11 12:44:12 -07:00
Arafatkatze 9a44996e64 Adding multi root to cli 2025-10-11 12:44:12 -07:00
7 changed files with 262 additions and 16 deletions
+5
View File
@@ -135,6 +135,11 @@ func newTaskNewCommand() *cobra.Command {
settings = append(settings, "yolo_mode_toggled=true")
}
// Debug: Log workspaces before creating task
if global.Config.Verbose {
fmt.Printf("[DEBUG]: Workspaces: %v\n", workspaces)
}
// Create the task
taskID, err := taskManager.CreateTask(ctx, prompt, images, files, workspaces, settings)
if err != nil {
+22 -5
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"sync"
"time"
@@ -111,6 +112,16 @@ func (m *Manager) CreateTask(ctx context.Context, prompt string, images, files [
m.mu.Lock()
defer m.mu.Unlock()
// Resolve workspace paths to absolute paths
absoluteWorkspacePaths := make([]string, len(workspacePaths))
for i, workspacePath := range workspacePaths {
absPath, err := filepath.Abs(workspacePath)
if err != nil {
return "", fmt.Errorf("failed to resolve workspace path '%s': %w", workspacePath, err)
}
absoluteWorkspacePaths[i] = absPath
}
if global.Config.Verbose {
m.renderer.RenderDebug("Creating task: %s", prompt)
if len(files) > 0 {
@@ -120,7 +131,8 @@ func (m *Manager) CreateTask(ctx context.Context, prompt string, images, files [
m.renderer.RenderDebug("Images: %v", images)
}
if len(workspacePaths) > 0 {
m.renderer.RenderDebug("Workspaces: %v", workspacePaths)
m.renderer.RenderDebug("Workspaces (original): %v", workspacePaths)
m.renderer.RenderDebug("Workspaces (absolute): %v", absoluteWorkspacePaths)
}
if len(settingsFlags) > 0 {
m.renderer.RenderDebug("Settings: %v", settingsFlags)
@@ -144,12 +156,17 @@ func (m *Manager) CreateTask(ctx context.Context, prompt string, images, files [
// Create task request
req := &cline.NewTaskRequest{
Text: prompt,
Images: images,
Files: files,
TaskSettings: taskSettings,
Text: prompt,
Images: images,
Files: files,
TaskSettings: taskSettings,
WorkspacePaths: absoluteWorkspacePaths,
}
// Debug: Log the request details (ALWAYS - for debugging)
fmt.Printf("[DEBUG-GRPC]: WorkspacePaths in struct: %v (len=%d)\n", req.WorkspacePaths, len(req.WorkspacePaths))
fmt.Printf("[DEBUG-GRPC]: absoluteWorkspacePaths var: %v (len=%d)\n", absoluteWorkspacePaths, len(absoluteWorkspacePaths))
resp, err := m.client.Task.NewTask(ctx, req)
if err != nil {
return "", fmt.Errorf("failed to create task: %w", err)
+1
View File
@@ -45,6 +45,7 @@ message NewTaskRequest {
repeated string images = 3;
repeated string files = 4;
optional Settings task_settings = 5;
repeated string workspace_paths = 6;
}
// Request message for toggling task favorite status
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'
# Configuration
INSTALL_DIR="${CLINE_INSTALL_DIR:-$HOME/.cline/cli}"
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
echo ""
echo -e "${MAGENTA}${BOLD}Installing Cline CLI from local build${NC}"
echo ""
# Always rebuild CLI to ensure latest changes
echo -e "${CYAN}${NC} ${DIM}Rebuilding CLI binaries...${NC}"
cd "$PROJECT_ROOT"
npm run compile-cli > /dev/null 2>&1
echo -e "${GREEN}${NC} CLI binaries rebuilt"
# Always rebuild standalone to ensure latest cline-core.js
echo -e "${CYAN}${NC} ${DIM}Rebuilding standalone package (this may take ~30 seconds)...${NC}"
npm run compile-standalone > /dev/null 2>&1
echo -e "${GREEN}${NC} Standalone package rebuilt"
echo ""
echo -e "${CYAN}${NC} ${DIM}Installing to $INSTALL_DIR${NC}"
# Remove existing installation (clean install)
# This ensures no conflicts with old versions and guarantees a fresh state
if [ -d "$INSTALL_DIR" ]; then
echo -e "${YELLOW}${NC} ${DIM}Removing existing installation for clean install${NC}"
rm -rf "$INSTALL_DIR"
fi
# Create installation directory
mkdir -p "$INSTALL_DIR/bin"
# Copy standalone package first (includes node_modules, cline-core.js, etc.)
rsync -a --exclude='bin' "$PROJECT_ROOT/dist-standalone/" "$INSTALL_DIR/"
# Detect platform for native modules
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m)
if [[ "$arch" == "aarch64" ]]; then arch="arm64"; fi
if [[ "$arch" == "x86_64" ]]; then arch="x64"; fi
platform="$os-$arch"
# Copy platform-specific native modules (like better-sqlite3)
if [ -d "$PROJECT_ROOT/dist-standalone/binaries/$platform/node_modules" ]; then
echo -e "${CYAN}${NC} ${DIM}Installing platform-specific modules for $platform${NC}"
cp -r "$PROJECT_ROOT/dist-standalone/binaries/$platform/node_modules/"* "$INSTALL_DIR/node_modules/" 2>/dev/null || true
fi
# Copy binaries (this will create/overwrite the bin directory)
mkdir -p "$INSTALL_DIR/bin"
cp "$PROJECT_ROOT/cli/bin/cline" "$INSTALL_DIR/bin/"
cp "$PROJECT_ROOT/cli/bin/cline-host" "$INSTALL_DIR/bin/"
# Use system Node.js (symlink to avoid copying large binary)
if command -v node >/dev/null 2>&1; then
ln -sf "$(which node)" "$INSTALL_DIR/bin/node"
echo -e "${GREEN}${NC} Linked to system Node.js: $(node --version)"
else
echo -e "${YELLOW}${NC} Node.js not found in PATH. Please install Node.js."
exit 1
fi
# Make binaries executable
chmod +x "$INSTALL_DIR/bin/cline"
chmod +x "$INSTALL_DIR/bin/cline-host"
chmod +x "$INSTALL_DIR/bin/node" 2>/dev/null || true
# Rebuild better-sqlite3 for system Node.js version
echo -e "${CYAN}${NC} ${DIM}Rebuilding native modules for Node.js $(node --version)...${NC}"
cd "$INSTALL_DIR"
npm rebuild better-sqlite3 > /dev/null 2>&1
cd "$PROJECT_ROOT"
echo -e "${GREEN}${NC} Native modules rebuilt"
echo -e "${GREEN}${NC} Installed to ${MAGENTA}${BOLD}$INSTALL_DIR${NC}"
# Configure PATH
BIN_DIR="$INSTALL_DIR/bin"
SHELL_CONFIG="$HOME/.zshrc"
if [ -f "$HOME/.bashrc" ]; then
SHELL_CONFIG="$HOME/.bashrc"
fi
if ! grep -q "$BIN_DIR" "$SHELL_CONFIG" 2>/dev/null; then
echo "" >> "$SHELL_CONFIG"
echo "# Cline CLI" >> "$SHELL_CONFIG"
echo "export PATH=\"$BIN_DIR:\$PATH\"" >> "$SHELL_CONFIG"
echo -e "${GREEN}${NC} Added to PATH in ${CYAN}$(basename $SHELL_CONFIG)${NC}"
else
echo -e "${GREEN}${NC} Already in PATH"
fi
echo ""
echo -e "${GREEN}${BOLD}Installation complete!${NC}"
echo ""
echo -e "Run this to start using ${MAGENTA}${BOLD}cline${NC} immediately:"
echo ""
echo -e "${YELLOW}${BOLD} exec \$SHELL${NC}"
echo ""
echo -e "${DIM}(or just open a new terminal window)${NC}"
echo ""
+88 -4
View File
@@ -2,6 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { buildApiHandler } from "@core/api"
import { detectWorkspaceRoots } from "@core/workspace/detection"
import { setupWorkspaceManager } from "@core/workspace/setup"
import { VcsType, WorkspaceRoot } from "@core/workspace/WorkspaceRoot"
import { WorkspaceRootManager } from "@core/workspace/WorkspaceRootManager"
import { cleanupLegacyCheckpoints } from "@integrations/checkpoints/CheckpointMigration"
import { downloadTask } from "@integrations/misc/export-markdown"
@@ -32,6 +33,7 @@ import { getDistinctId } from "@/services/logging/distinctId"
import { telemetryService } from "@/services/telemetry"
import { ShowMessageType } from "@/shared/proto/host/window"
import { getLatestAnnouncementId } from "@/utils/announcements"
import { getLatestGitCommitHash } from "@/utils/git"
import { getCwd, getDesktopDir } from "@/utils/path"
import { PromptRegistry } from "../prompts/system-prompt"
import {
@@ -87,6 +89,80 @@ export class Controller {
return this.workspaceManager
}
/**
* Initialize WorkspaceRootManager from provided workspace paths (for CLI usage)
* @param workspacePaths Array of workspace directory paths
* @returns Initialized WorkspaceRootManager
*/
private async initializeWorkspaceManagerFromPaths(workspacePaths: string[]): Promise<WorkspaceRootManager> {
console.log("[DEBUG] initializeWorkspaceManagerFromPaths called with:", workspacePaths)
const startTime = performance.now()
const cwd = await getCwd(getDesktopDir())
try {
const roots: WorkspaceRoot[] = []
for (const workspacePath of workspacePaths) {
// Resolve to absolute path
const absolutePath = path.resolve(workspacePath)
console.log("[DEBUG] Resolving workspace path:", workspacePath, "->", absolutePath)
// Detect VCS for this workspace (using detection module's public function)
const vcs = await detectWorkspaceRoots()
.then((roots) => roots.find((r) => r.path === absolutePath)?.vcs ?? VcsType.None)
.catch(() => VcsType.None)
// Get commit hash if Git repo (handle null return)
const gitHash = vcs === VcsType.Git ? await getLatestGitCommitHash(absolutePath) : null
const commitHash = gitHash === null ? undefined : gitHash
roots.push({
path: absolutePath,
name: path.basename(absolutePath),
vcs,
commitHash,
})
}
// First path is primary workspace
const manager = new WorkspaceRootManager(roots, 0)
console.log(`[DEBUG] WorkspaceManager created with roots:`, roots)
console.log(`[DEBUG] Primary workspace:`, roots[0])
console.log(`[WorkspaceManager] Initialized from CLI with ${roots.length} workspace(s)`)
// Telemetry
telemetryService.captureWorkspaceInitialized(
roots.length,
roots.map((r) => r.vcs.toString()),
performance.now() - startTime,
true,
)
// Persist
this.stateManager.setGlobalState("workspaceRoots", manager.getRoots())
this.stateManager.setGlobalState("primaryRootIndex", manager.getPrimaryIndex())
return manager
} catch (error) {
// Fallback to single-root from cwd on error
console.error("[WorkspaceManager] Failed to initialize from paths:", error)
telemetryService.captureWorkspaceInitError(error as Error, true, workspacePaths.length)
const manager = await WorkspaceRootManager.fromLegacyCwd(cwd)
const roots = manager.getRoots()
this.stateManager.setGlobalState("workspaceRoots", roots)
this.stateManager.setGlobalState("primaryRootIndex", manager.getPrimaryIndex())
HostProvider.window.showMessage({
type: ShowMessageType.WARNING,
message: "Failed to initialize workspaces from provided paths. Using current directory.",
})
return manager
}
}
constructor(readonly context: vscode.ExtensionContext) {
PromptRegistry.getInstance() // Ensure prompts and tools are registered
HostProvider.get().logToChannel("ClineProvider instantiated")
@@ -199,7 +275,9 @@ export class Controller {
files?: string[],
historyItem?: HistoryItem,
taskSettings?: Partial<Settings>,
workspacePaths?: string[],
) {
console.log("[DEBUG] Controller.initTask called with workspacePaths:", workspacePaths)
await this.clearTask() // ensures that an existing task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one
const autoApprovalSettings = this.stateManager.getGlobalSettingsKey("autoApprovalSettings")
@@ -227,10 +305,16 @@ export class Controller {
}
// Initialize and persist the workspace manager (multi-root or single-root) with telemetry + fallback
this.workspaceManager = await setupWorkspaceManager({
stateManager: this.stateManager,
detectRoots: detectWorkspaceRoots,
})
// If workspace paths are provided from CLI, use them; otherwise detect from VSCode
if (workspacePaths && workspacePaths.length > 0) {
this.workspaceManager = await this.initializeWorkspaceManagerFromPaths(workspacePaths)
} else {
console.log("[DEBUG] Controller.initTask: No workspace paths provided, using detectWorkspaceRoots")
this.workspaceManager = await setupWorkspaceManager({
stateManager: this.stateManager,
detectRoots: detectWorkspaceRoots,
})
}
const cwd = this.workspaceManager?.getPrimaryRoot()?.path || (await getCwd(getDesktopDir()))
+10 -1
View File
@@ -14,6 +14,8 @@ import { Controller } from ".."
* @returns Empty response
*/
export async function newTask(controller: Controller, request: NewTaskRequest): Promise<String> {
console.log("[DEBUG] newTask received request.workspacePaths:", request.workspacePaths)
const convertOpenaiReasoningEffort = (effort: ProtoOpenaiReasoningEffort): string => {
switch (effort) {
case ProtoOpenaiReasoningEffort.LOW:
@@ -70,6 +72,13 @@ export async function newTask(controller: Controller, request: NewTaskRequest):
}).filter(([_, value]) => value !== undefined),
)
const taskId = await controller.initTask(request.text, request.images, request.files, undefined, filteredTaskSettings)
const taskId = await controller.initTask(
request.text,
request.images,
request.files,
undefined,
filteredTaskSettings,
request.workspacePaths,
)
return String.create({ value: taskId || "" })
}
+22 -6
View File
@@ -4,15 +4,31 @@ import type { StateManager } from "../storage/StateManager"
/**
* Determines if multi-root workspace mode should be enabled.
*
* Multi-root is enabled only when BOTH conditions are true:
* 1. The feature flag is enabled (server-side control)
* 2. The user has opted in via their settings (user preference)
* Multi-root is enabled when:
* 1. Running in standalone/CLI mode (always enabled for CLI usage), OR
* 2. Both the feature flag AND user setting are enabled (for VSCode extension)
*
* @param stateManager - The state manager to check user preferences
* @returns true if both feature flag and user setting are enabled
* @param forceEnable - Optional flag to force enable (used when CLI provides workspace paths)
* @returns true if multi-root should be enabled
*/
export function isMultiRootEnabled(stateManager: StateManager): boolean {
export function isMultiRootEnabled(stateManager: StateManager, forceEnable?: boolean): boolean {
// If explicitly forced (e.g., CLI provided workspace paths), always enable
if (forceEnable) {
return true
}
// Check if running in standalone mode (CLI)
// In standalone mode, we always enable multi-root since the CLI explicitly provides workspace paths
console.log(" DEBUG: isMultiRootEnabled: isStandalone:", (global as any).standaloneTerminalManager)
const isStandalone = typeof (global as any).standaloneTerminalManager !== "undefined"
console.log(" DEBUG: isMultiRootEnabled: isStandalone", isStandalone)
if (isStandalone) {
return true
}
// For VSCode extension, require both feature flag and user setting
const featureFlag = featureFlagsService.getMultiRootEnabled()
const userSetting = stateManager.getGlobalStateKey("multiRootEnabled")
return featureFlag && !!userSetting
return featureFlag && !userSetting
}