From 6453702bf9e606c9249d76cc646e6d2a513a7663 Mon Sep 17 00:00:00 2001 From: Haze <709547807@qq.com> Date: Wed, 25 Feb 2026 14:22:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(electron):=20integrate=20ClawX=20context?= =?UTF-8?q?=20into=20openclaw=20workspace=20initi=E2=80=A6=20(#161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Haze --- electron/main/index.ts | 8 ++ electron/utils/openclaw-workspace.ts | 122 +++++++++++++++++++++++++++ resources/context/AGENTS.clawx.md | 8 ++ resources/context/TOOLS.clawx.md | 13 +++ 4 files changed, 151 insertions(+) create mode 100644 electron/utils/openclaw-workspace.ts create mode 100644 resources/context/AGENTS.clawx.md create mode 100644 resources/context/TOOLS.clawx.md diff --git a/electron/main/index.ts b/electron/main/index.ts index 4b8cc30ef..198f58c1e 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -14,6 +14,7 @@ import { logger } from '../utils/logger'; import { warmupNetworkOptimization } from '../utils/uv-env'; import { ClawHubService } from '../gateway/clawhub'; +import { ensureClawXContext } from '../utils/openclaw-workspace'; // Disable GPU acceleration for better compatibility app.disableHardwareAcceleration(); @@ -177,6 +178,13 @@ async function initialize(): Promise { mainWindow = null; }); + // Merge ClawX context snippets into the openclaw workspace bootstrap files + try { + ensureClawXContext(); + } catch (error) { + logger.warn('Failed to merge ClawX context into workspace:', error); + } + // Start Gateway automatically try { logger.debug('Auto-starting Gateway...'); diff --git a/electron/utils/openclaw-workspace.ts b/electron/utils/openclaw-workspace.ts new file mode 100644 index 000000000..035bb70f8 --- /dev/null +++ b/electron/utils/openclaw-workspace.ts @@ -0,0 +1,122 @@ +import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from 'fs'; +import { join } from 'path'; +import { homedir } from 'os'; +import { logger } from './logger'; +import { getResourcesDir } from './paths'; + +const CLAWX_BEGIN = ''; +const CLAWX_END = ''; + +/** + * Merge a ClawX context section into an existing file's content. + * If markers already exist, replaces the section in-place. + * Otherwise appends it at the end. + */ +export function mergeClawXSection(existing: string, section: string): string { + const wrapped = `${CLAWX_BEGIN}\n${section.trim()}\n${CLAWX_END}`; + const beginIdx = existing.indexOf(CLAWX_BEGIN); + const endIdx = existing.indexOf(CLAWX_END); + if (beginIdx !== -1 && endIdx !== -1) { + return existing.slice(0, beginIdx) + wrapped + existing.slice(endIdx + CLAWX_END.length); + } + return existing.trimEnd() + '\n\n' + wrapped + '\n'; +} + +/** + * Collect all unique workspace directories from the openclaw config: + * the defaults workspace, each agent's workspace, and any workspace-* + * directories that already exist under ~/.openclaw/. + */ +function resolveAllWorkspaceDirs(): string[] { + const openclawDir = join(homedir(), '.openclaw'); + const dirs = new Set(); + + const configPath = join(openclawDir, 'openclaw.json'); + try { + if (existsSync(configPath)) { + const config = JSON.parse(readFileSync(configPath, 'utf-8')); + + const defaultWs = config?.agents?.defaults?.workspace; + if (typeof defaultWs === 'string' && defaultWs.trim()) { + dirs.add(defaultWs.replace(/^~/, homedir())); + } + + const agents = config?.agents?.list; + if (Array.isArray(agents)) { + for (const agent of agents) { + const ws = agent?.workspace; + if (typeof ws === 'string' && ws.trim()) { + dirs.add(ws.replace(/^~/, homedir())); + } + } + } + } + } catch { + // ignore config parse errors + } + + try { + for (const entry of readdirSync(openclawDir, { withFileTypes: true })) { + if (entry.isDirectory() && entry.name.startsWith('workspace')) { + dirs.add(join(openclawDir, entry.name)); + } + } + } catch { + // ignore read errors + } + + if (dirs.size === 0) { + dirs.add(join(openclawDir, 'workspace')); + } + + return [...dirs]; +} + +/** + * Ensure ClawX context snippets are merged into the openclaw workspace + * bootstrap files. Reads `*.clawx.md` templates from resources/context/ + * and injects them as marker-delimited sections into the corresponding + * workspace `.md` files (e.g. AGENTS.clawx.md -> AGENTS.md). + * + * Iterates over every discovered agent workspace so all agents receive + * the ClawX context regardless of which one is active. + */ +export function ensureClawXContext(): void { + const contextDir = join(getResourcesDir(), 'context'); + if (!existsSync(contextDir)) { + logger.debug('ClawX context directory not found, skipping context merge'); + return; + } + + let files: string[]; + try { + files = readdirSync(contextDir).filter((f) => f.endsWith('.clawx.md')); + } catch { + return; + } + + const workspaceDirs = resolveAllWorkspaceDirs(); + + for (const workspaceDir of workspaceDirs) { + if (!existsSync(workspaceDir)) { + mkdirSync(workspaceDir, { recursive: true }); + } + + for (const file of files) { + const targetName = file.replace('.clawx.md', '.md'); + const targetPath = join(workspaceDir, targetName); + const section = readFileSync(join(contextDir, file), 'utf-8'); + + let existing = ''; + if (existsSync(targetPath)) { + existing = readFileSync(targetPath, 'utf-8'); + } + + const merged = mergeClawXSection(existing, section); + if (merged !== existing) { + writeFileSync(targetPath, merged, 'utf-8'); + logger.info(`Merged ClawX context into ${targetName} (${workspaceDir})`); + } + } + } +} diff --git a/resources/context/AGENTS.clawx.md b/resources/context/AGENTS.clawx.md new file mode 100644 index 000000000..d30cea345 --- /dev/null +++ b/resources/context/AGENTS.clawx.md @@ -0,0 +1,8 @@ +## ClawX Environment + +You are ClawX, a desktop AI assistant application based on OpenClaw. + +- **Python**: Always use `uv` to run Python commands. The `uv` binary is bundled and available on PATH. Examples: `uv run python script.py`, `uv pip install package`. +- **Browser**: When asked to open URLs or web pages, use the browser tool to open them in the user's system default browser. +- **Shell**: You have full shell access on the user's machine. Prefer using tools directly over asking the user to run commands manually. +- Always confirm before running destructive operations. diff --git a/resources/context/TOOLS.clawx.md b/resources/context/TOOLS.clawx.md new file mode 100644 index 000000000..72977554d --- /dev/null +++ b/resources/context/TOOLS.clawx.md @@ -0,0 +1,13 @@ +## ClawX Tool Notes + +### uv (Python) + +- `uv` is the default Python environment manager. It is bundled with ClawX and on PATH. +- Use `uv run python