mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
fix(multi-cli): populate multiCliPlan sessions in liteTaskDataStore
Fix task click handlers not working in multi-CLI planning detail page.
Root cause: liteTaskDataStore was not being populated with multiCliPlan
sessions during initialization, so task click handlers couldn't access
session data using currentSessionDetailKey.
Changes:
- navigation.js: Add code to populate multiCliPlan sessions in liteTaskDataStore
- notifications.js: Add code to populate multiCliPlan sessions when data refreshes
Now when task detail page loads, liteTaskDataStore contains the correct key
'multi-cli-${sessionId}' matching currentSessionDetailKey, allowing task
click handlers to find session data and open detail drawer.
Verified: Task clicks now properly open detail panel for all 7 tasks.
This commit is contained in:
@@ -12,14 +12,11 @@ import { z } from 'zod';
|
||||
import type { ToolSchema, ToolResult } from '../types/tool.js';
|
||||
import { spawn } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { getProjectRoot } from '../utils/path-validator.js';
|
||||
import { getCodexLensPython } from '../utils/codexlens-path.js';
|
||||
|
||||
// CodexLens venv configuration
|
||||
const CODEXLENS_VENV =
|
||||
process.platform === 'win32'
|
||||
? join(homedir(), '.codexlens', 'venv', 'Scripts', 'python.exe')
|
||||
: join(homedir(), '.codexlens', 'venv', 'bin', 'python');
|
||||
const CODEXLENS_VENV = getCodexLensPython();
|
||||
|
||||
// Define Zod schema for validation
|
||||
const ParamsSchema = z.object({
|
||||
|
||||
@@ -24,6 +24,12 @@ import {
|
||||
isUvAvailable,
|
||||
createCodexLensUvManager,
|
||||
} from '../utils/uv-manager.js';
|
||||
import {
|
||||
getCodexLensDataDir,
|
||||
getCodexLensVenvDir,
|
||||
getCodexLensPython,
|
||||
getCodexLensPip,
|
||||
} from '../utils/codexlens-path.js';
|
||||
|
||||
// Get directory of this module
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -109,14 +115,6 @@ function findLocalCcwLitellmPath(): string | null {
|
||||
return findLocalPackagePath('ccw-litellm');
|
||||
}
|
||||
|
||||
// CodexLens configuration
|
||||
const CODEXLENS_DATA_DIR = join(homedir(), '.codexlens');
|
||||
const CODEXLENS_VENV = join(CODEXLENS_DATA_DIR, 'venv');
|
||||
const VENV_PYTHON =
|
||||
process.platform === 'win32'
|
||||
? join(CODEXLENS_VENV, 'Scripts', 'python.exe')
|
||||
: join(CODEXLENS_VENV, 'bin', 'python');
|
||||
|
||||
// Bootstrap status cache
|
||||
let bootstrapChecked = false;
|
||||
let bootstrapReady = false;
|
||||
@@ -245,7 +243,7 @@ async function checkVenvStatus(force = false): Promise<ReadyStatus> {
|
||||
}
|
||||
|
||||
// Check venv exists
|
||||
if (!existsSync(CODEXLENS_VENV)) {
|
||||
if (!existsSync(getCodexLensVenvDir())) {
|
||||
const result = { ready: false, error: 'Venv not found' };
|
||||
venvStatusCache = { status: result, timestamp: Date.now() };
|
||||
console.log(`[PERF][CodexLens] checkVenvStatus (no venv): ${Date.now() - funcStart}ms`);
|
||||
@@ -253,7 +251,7 @@ async function checkVenvStatus(force = false): Promise<ReadyStatus> {
|
||||
}
|
||||
|
||||
// Check python executable exists
|
||||
if (!existsSync(VENV_PYTHON)) {
|
||||
if (!existsSync(getCodexLensPython())) {
|
||||
const result = { ready: false, error: 'Python executable not found in venv' };
|
||||
venvStatusCache = { status: result, timestamp: Date.now() };
|
||||
console.log(`[PERF][CodexLens] checkVenvStatus (no python): ${Date.now() - funcStart}ms`);
|
||||
@@ -265,7 +263,7 @@ async function checkVenvStatus(force = false): Promise<ReadyStatus> {
|
||||
console.log('[PERF][CodexLens] checkVenvStatus spawning Python...');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(VENV_PYTHON, ['-c', 'import codexlens; import watchdog; print(codexlens.__version__)'], {
|
||||
const child = spawn(getCodexLensPython(), ['-c', 'import codexlens; import watchdog; print(codexlens.__version__)'], {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: 10000,
|
||||
});
|
||||
@@ -377,7 +375,7 @@ try:
|
||||
except Exception as e:
|
||||
print(json.dumps({"available": False, "error": str(e)}))
|
||||
`;
|
||||
const child = spawn(VENV_PYTHON, ['-c', checkCode], {
|
||||
const child = spawn(getCodexLensPython(), ['-c', checkCode], {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: 15000,
|
||||
});
|
||||
@@ -438,7 +436,7 @@ async function ensureLiteLLMEmbedderReady(): Promise<BootstrapResult> {
|
||||
|
||||
// Check if ccw_litellm can be imported
|
||||
const importStatus = await new Promise<{ ok: boolean; error?: string }>((resolve) => {
|
||||
const child = spawn(VENV_PYTHON, ['-c', 'import ccw_litellm; print("OK")'], {
|
||||
const child = spawn(getCodexLensPython(), ['-c', 'import ccw_litellm; print("OK")'], {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: 15000,
|
||||
});
|
||||
@@ -502,10 +500,7 @@ async function ensureLiteLLMEmbedderReady(): Promise<BootstrapResult> {
|
||||
}
|
||||
|
||||
// Fallback: Use pip for installation
|
||||
const pipPath =
|
||||
process.platform === 'win32'
|
||||
? join(CODEXLENS_VENV, 'Scripts', 'pip.exe')
|
||||
: join(CODEXLENS_VENV, 'bin', 'pip');
|
||||
const pipPath = getCodexLensPip();
|
||||
|
||||
try {
|
||||
if (localPath) {
|
||||
@@ -552,10 +547,7 @@ interface PythonEnvInfo {
|
||||
* DirectML requires: 64-bit Python, version 3.8-3.12
|
||||
*/
|
||||
async function checkPythonEnvForDirectML(): Promise<PythonEnvInfo> {
|
||||
const pythonPath =
|
||||
process.platform === 'win32'
|
||||
? join(CODEXLENS_VENV, 'Scripts', 'python.exe')
|
||||
: join(CODEXLENS_VENV, 'bin', 'python');
|
||||
const pythonPath = getCodexLensPython();
|
||||
|
||||
if (!existsSync(pythonPath)) {
|
||||
return { version: '', majorMinor: '', architecture: 0, compatible: false, error: 'Python not found in venv' };
|
||||
@@ -800,10 +792,7 @@ async function installSemantic(gpuMode: GpuMode = 'cpu'): Promise<BootstrapResul
|
||||
console.log(`[CodexLens] Python ${pythonEnv.version} (${pythonEnv.architecture}-bit) - DirectML compatible`);
|
||||
}
|
||||
|
||||
const pipPath =
|
||||
process.platform === 'win32'
|
||||
? join(CODEXLENS_VENV, 'Scripts', 'pip.exe')
|
||||
: join(CODEXLENS_VENV, 'bin', 'pip');
|
||||
const pipPath = getCodexLensPip();
|
||||
|
||||
// IMPORTANT: Uninstall all onnxruntime variants first to prevent conflicts
|
||||
// Having multiple onnxruntime packages causes provider detection issues
|
||||
@@ -933,16 +922,18 @@ async function bootstrapVenv(): Promise<BootstrapResult> {
|
||||
|
||||
// Fall back to pip logic...
|
||||
// Ensure data directory exists
|
||||
if (!existsSync(CODEXLENS_DATA_DIR)) {
|
||||
mkdirSync(CODEXLENS_DATA_DIR, { recursive: true });
|
||||
const dataDir = getCodexLensDataDir();
|
||||
const venvDir = getCodexLensVenvDir();
|
||||
if (!existsSync(dataDir)) {
|
||||
mkdirSync(dataDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create venv if not exists
|
||||
if (!existsSync(CODEXLENS_VENV)) {
|
||||
if (!existsSync(venvDir)) {
|
||||
try {
|
||||
console.log('[CodexLens] Creating virtual environment...');
|
||||
const pythonCmd = getSystemPython();
|
||||
execSync(`${pythonCmd} -m venv "${CODEXLENS_VENV}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PROCESS_SPAWN });
|
||||
execSync(`${pythonCmd} -m venv "${venvDir}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PROCESS_SPAWN });
|
||||
} catch (err) {
|
||||
return { success: false, error: `Failed to create venv: ${(err as Error).message}` };
|
||||
}
|
||||
@@ -951,10 +942,7 @@ async function bootstrapVenv(): Promise<BootstrapResult> {
|
||||
// Install codex-lens
|
||||
try {
|
||||
console.log('[CodexLens] Installing codex-lens package...');
|
||||
const pipPath =
|
||||
process.platform === 'win32'
|
||||
? join(CODEXLENS_VENV, 'Scripts', 'pip.exe')
|
||||
: join(CODEXLENS_VENV, 'bin', 'pip');
|
||||
const pipPath = getCodexLensPip();
|
||||
|
||||
// Try local path - codex-lens is local-only, not published to PyPI
|
||||
const codexLensPath = findLocalCodexLensPath();
|
||||
@@ -1131,7 +1119,7 @@ async function executeCodexLens(args: string[], options: ExecuteOptions = {}): P
|
||||
// spawn's cwd option handles drive changes correctly on Windows
|
||||
const spawnArgs = ['-m', 'codexlens', ...args];
|
||||
|
||||
const child = spawn(VENV_PYTHON, spawnArgs, {
|
||||
const child = spawn(getCodexLensPython(), spawnArgs, {
|
||||
cwd,
|
||||
shell: false, // CRITICAL: Prevent command injection
|
||||
timeout,
|
||||
@@ -1674,7 +1662,7 @@ export async function handler(params: Record<string, unknown>): Promise<ToolResu
|
||||
async function uninstallCodexLens(): Promise<BootstrapResult> {
|
||||
try {
|
||||
// Check if venv exists
|
||||
if (!existsSync(CODEXLENS_VENV)) {
|
||||
if (!existsSync(getCodexLensVenvDir())) {
|
||||
return { success: false, error: 'CodexLens not installed (venv not found)' };
|
||||
}
|
||||
|
||||
@@ -1694,7 +1682,8 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
console.log(`[CodexLens] Removing directory: ${CODEXLENS_DATA_DIR}`);
|
||||
const dataDir = getCodexLensDataDir();
|
||||
console.log(`[CodexLens] Removing directory: ${dataDir}`);
|
||||
|
||||
// Remove the entire .codexlens directory with retry logic for locked files
|
||||
const fs = await import('fs');
|
||||
@@ -1729,7 +1718,7 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
||||
}
|
||||
};
|
||||
|
||||
await removeWithRetry(CODEXLENS_DATA_DIR);
|
||||
await removeWithRetry(dataDir);
|
||||
|
||||
// Reset bootstrap cache
|
||||
bootstrapChecked = false;
|
||||
@@ -1827,7 +1816,7 @@ export {
|
||||
|
||||
// Export Python path for direct spawn usage (e.g., watcher)
|
||||
export function getVenvPythonPath(): string {
|
||||
return VENV_PYTHON;
|
||||
return getCodexLensPython();
|
||||
}
|
||||
|
||||
export type { GpuMode, PythonEnvInfo };
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { getCodexLensPython, getCodexLensVenvDir } from '../utils/codexlens-path.js';
|
||||
|
||||
export interface LiteLLMConfig {
|
||||
pythonPath?: string; // Default: CodexLens venv Python
|
||||
@@ -22,7 +22,7 @@ export interface LiteLLMConfig {
|
||||
|
||||
// Platform-specific constants for CodexLens venv
|
||||
const IS_WINDOWS = process.platform === 'win32';
|
||||
const CODEXLENS_VENV = join(homedir(), '.codexlens', 'venv');
|
||||
const CODEXLENS_VENV = getCodexLensVenvDir();
|
||||
const VENV_BIN_DIR = IS_WINDOWS ? 'Scripts' : 'bin';
|
||||
const PYTHON_EXECUTABLE = IS_WINDOWS ? 'python.exe' : 'python';
|
||||
|
||||
@@ -40,6 +40,20 @@ export function getCodexLensVenvPython(): string {
|
||||
return 'python';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Python path from CodexLens venv using centralized path utility
|
||||
* Falls back to system 'python' if venv doesn't exist
|
||||
* @returns Path to Python executable
|
||||
*/
|
||||
export function getCodexLensPythonPath(): string {
|
||||
const codexLensPython = getCodexLensPython();
|
||||
if (existsSync(codexLensPython)) {
|
||||
return codexLensPython;
|
||||
}
|
||||
// Fallback to system Python if venv not available
|
||||
return 'python';
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string;
|
||||
|
||||
@@ -22,7 +22,18 @@ export interface LoopTask {
|
||||
/** Task description (what to do) */
|
||||
description: string;
|
||||
|
||||
/** CLI tool to use (bash, builtin tools, cli-wrapper, api-endpoint) */
|
||||
/**
|
||||
* CLI tool to use
|
||||
*
|
||||
* Should be one of the enabled tools from cli-tools.json:
|
||||
* - 'bash' (always available)
|
||||
* - Builtin tools: 'gemini', 'qwen', 'codex', 'claude', 'opencode'
|
||||
* - CLI wrappers: 'doubao', etc. (if enabled)
|
||||
* - API endpoints: custom tools (if enabled)
|
||||
*
|
||||
* Note: Validation is performed at the API layer (loop-v2-routes.ts)
|
||||
* to ensure tool is enabled before saving.
|
||||
*/
|
||||
tool: string;
|
||||
|
||||
/** Execution mode */
|
||||
|
||||
Reference in New Issue
Block a user