diff --git a/Makefile b/Makefile index 9a0833fcf..817d106fa 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ .PHONY: test test-unit test-integration test-watch test-coverage test-e2e .PHONY: dist dist-mac dist-win dist-linux .PHONY: docs docs-build docs-watch +.PHONY: ensure-deps # Prettier patterns for formatting PRETTIER_PATTERNS := 'src/**/*.{ts,tsx,json}' 'tests/**/*.{ts,json}' 'docs/**/*.{md,mdx}' '*.{json,md}' @@ -32,6 +33,13 @@ TS_SOURCES := $(shell find src -type f \( -name '*.ts' -o -name '*.tsx' \)) # Default target all: build +# Ensure dependencies are installed +ensure-deps: + @if [ ! -d "node_modules" ]; then \ + echo "node_modules not found, running bun install..."; \ + bun install; \ + fi + ## Help help: ## Show this help message @echo 'Usage: make [target]' @@ -40,7 +48,7 @@ help: ## Show this help message @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' ## Development -dev: build-main ## Start development server (Vite + TypeScript watcher) +dev: ensure-deps build-main ## Start development server (Vite + TypeScript watcher) @bun x concurrently -k \ "bun x concurrently \"bun x tsc -w -p tsconfig.main.json\" \"bun x tsc-alias -w -p tsconfig.main.json\"" \ "vite" @@ -49,16 +57,16 @@ start: build-main build-preload ## Build and start Electron app @bun x electron --remote-debugging-port=9222 . ## Build targets (can run in parallel) -build: src/version.ts build-renderer build-main build-preload ## Build all targets +build: ensure-deps src/version.ts build-renderer build-main build-preload ## Build all targets -build-main: dist/main.js ## Build main process +build-main: ensure-deps dist/main.js ## Build main process dist/main.js: src/version.ts tsconfig.main.json tsconfig.json $(TS_SOURCES) @echo "Building main process..." @NODE_ENV=production bun x tsc -p tsconfig.main.json @NODE_ENV=production bun x tsc-alias -p tsconfig.main.json -build-preload: dist/preload.js ## Build preload script +build-preload: ensure-deps dist/preload.js ## Build preload script dist/preload.js: src/preload.ts $(TS_SOURCES) @echo "Building preload script..." @@ -69,7 +77,7 @@ dist/preload.js: src/preload.ts $(TS_SOURCES) --sourcemap=inline \ --outfile=dist/preload.js -build-renderer: src/version.ts ## Build renderer process +build-renderer: ensure-deps src/version.ts ## Build renderer process @echo "Building renderer..." @bun x vite build diff --git a/src/components/GitStatusIndicator.tsx b/src/components/GitStatusIndicator.tsx index 0e82044f7..8c7fd2ffb 100644 --- a/src/components/GitStatusIndicator.tsx +++ b/src/components/GitStatusIndicator.tsx @@ -324,6 +324,7 @@ ${getDirtyFiles} const result = await window.api.workspace.executeBash(workspaceId, script, { timeout_secs: 5, max_lines: 100, + niceness: 19, // Lowest priority - don't interfere with user operations }); if (!result.success) { diff --git a/src/contexts/GitStatusContext.tsx b/src/contexts/GitStatusContext.tsx index 578ad242e..b21e62115 100644 --- a/src/contexts/GitStatusContext.tsx +++ b/src/contexts/GitStatusContext.tsx @@ -79,6 +79,7 @@ export function GitStatusProvider({ workspaceMetadata, children }: GitStatusProv try { const result = await window.api.workspace.executeBash(workspaceId, GIT_FETCH_SCRIPT, { timeout_secs: 30, + niceness: 19, // Lowest priority - don't interfere with user operations }); if (!result.success) { @@ -204,6 +205,7 @@ export function GitStatusProvider({ workspaceMetadata, children }: GitStatusProv GIT_STATUS_SCRIPT, { timeout_secs: 5, + niceness: 19, // Lowest priority - don't interfere with user operations } ); diff --git a/src/services/ipcMain.ts b/src/services/ipcMain.ts index e023bd20f..073cf3160 100644 --- a/src/services/ipcMain.ts +++ b/src/services/ipcMain.ts @@ -757,7 +757,7 @@ export class IpcMain { _event, workspaceId: string, script: string, - options?: { timeout_secs?: number; max_lines?: number; stdin?: string } + options?: { timeout_secs?: number; max_lines?: number; stdin?: string; niceness?: number } ) => { try { // Get workspace metadata to find workspacePath @@ -778,6 +778,7 @@ export class IpcMain { const bashTool = createBashTool({ cwd: workspacePath, secrets: secretsToRecord(projectSecrets), + niceness: options?.niceness, }); // Execute the script with provided options diff --git a/src/services/tools/bash.ts b/src/services/tools/bash.ts index cc5427951..6a13be048 100644 --- a/src/services/tools/bash.ts +++ b/src/services/tools/bash.ts @@ -94,8 +94,16 @@ export const createBashTool: ToolFactory = (config: ToolConfiguration) => { } // Create the process with `using` for automatic cleanup + // If niceness is specified, wrap the command with nice + const finalScript = + config.niceness !== undefined + ? `nice -n ${config.niceness} bash -c ${JSON.stringify(script)}` + : script; + const finalCommand = + config.niceness !== undefined ? ["bash", "-c", finalScript] : ["bash", "-c", script]; + using childProcess = new DisposableProcess( - spawn("bash", ["-c", script], { + spawn(finalCommand[0], finalCommand.slice(1), { cwd: config.cwd, env: { ...process.env, diff --git a/src/types/ipc.ts b/src/types/ipc.ts index 6935536e1..53f766241 100644 --- a/src/types/ipc.ts +++ b/src/types/ipc.ts @@ -192,7 +192,7 @@ export interface IPCApi { executeBash( workspaceId: string, script: string, - options?: { timeout_secs?: number; max_lines?: number; stdin?: string } + options?: { timeout_secs?: number; max_lines?: number; stdin?: string; niceness?: number } ): Promise>; openTerminal(workspacePath: string): Promise; diff --git a/src/utils/tools/tools.ts b/src/utils/tools/tools.ts index 05b7fee1b..c026e16ef 100644 --- a/src/utils/tools/tools.ts +++ b/src/utils/tools/tools.ts @@ -18,6 +18,8 @@ export interface ToolConfiguration { cwd: string; /** Environment secrets to inject (optional) */ secrets?: Record; + /** Process niceness level (optional, -20 to 19, lower = higher priority) */ + niceness?: number; } /**