mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
Add comprehensive tests for vector/semantic search functionality
- Implement full coverage tests for Embedder model loading and embedding generation - Add CRUD operations and caching tests for VectorStore - Include cosine similarity computation tests - Validate semantic search accuracy and relevance through various queries - Establish performance benchmarks for embedding and search operations - Ensure edge cases and error handling are covered - Test thread safety and concurrent access scenarios - Verify availability of semantic search dependencies
This commit is contained in:
272
ccw/src/tools/cli-config-manager.ts
Normal file
272
ccw/src/tools/cli-config-manager.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* CLI Configuration Manager
|
||||
* Handles loading, saving, and managing CLI tool configurations
|
||||
* Stores config in .workflow/cli-config.json
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
// ========== Types ==========
|
||||
|
||||
export interface CliToolConfig {
|
||||
enabled: boolean;
|
||||
primaryModel: string; // For CLI endpoint calls (ccw cli exec)
|
||||
secondaryModel: string; // For internal calls (llm_enhancer, generate_module_docs)
|
||||
}
|
||||
|
||||
export interface CliConfig {
|
||||
version: number;
|
||||
tools: Record<string, CliToolConfig>;
|
||||
}
|
||||
|
||||
export type CliToolName = 'gemini' | 'qwen' | 'codex';
|
||||
|
||||
// ========== Constants ==========
|
||||
|
||||
export const PREDEFINED_MODELS: Record<CliToolName, string[]> = {
|
||||
gemini: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
|
||||
qwen: ['coder-model', 'vision-model', 'qwen2.5-coder-32b'],
|
||||
codex: ['gpt5-codex', 'gpt-4.1', 'o4-mini', 'o3']
|
||||
};
|
||||
|
||||
export const DEFAULT_CONFIG: CliConfig = {
|
||||
version: 1,
|
||||
tools: {
|
||||
gemini: {
|
||||
enabled: true,
|
||||
primaryModel: 'gemini-2.5-pro',
|
||||
secondaryModel: 'gemini-2.5-flash'
|
||||
},
|
||||
qwen: {
|
||||
enabled: true,
|
||||
primaryModel: 'coder-model',
|
||||
secondaryModel: 'coder-model'
|
||||
},
|
||||
codex: {
|
||||
enabled: true,
|
||||
primaryModel: 'gpt5-codex',
|
||||
secondaryModel: 'gpt5-codex'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CONFIG_DIR = '.workflow';
|
||||
const CONFIG_FILE = 'cli-config.json';
|
||||
|
||||
// ========== Helper Functions ==========
|
||||
|
||||
function getConfigPath(baseDir: string): string {
|
||||
return path.join(baseDir, CONFIG_DIR, CONFIG_FILE);
|
||||
}
|
||||
|
||||
function ensureConfigDir(baseDir: string): void {
|
||||
const configDir = path.join(baseDir, CONFIG_DIR);
|
||||
if (!fs.existsSync(configDir)) {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function isValidToolName(tool: string): tool is CliToolName {
|
||||
return ['gemini', 'qwen', 'codex'].includes(tool);
|
||||
}
|
||||
|
||||
function validateConfig(config: unknown): config is CliConfig {
|
||||
if (!config || typeof config !== 'object') return false;
|
||||
const c = config as Record<string, unknown>;
|
||||
|
||||
if (typeof c.version !== 'number') return false;
|
||||
if (!c.tools || typeof c.tools !== 'object') return false;
|
||||
|
||||
const tools = c.tools as Record<string, unknown>;
|
||||
for (const toolName of ['gemini', 'qwen', 'codex']) {
|
||||
const tool = tools[toolName];
|
||||
if (!tool || typeof tool !== 'object') return false;
|
||||
|
||||
const t = tool as Record<string, unknown>;
|
||||
if (typeof t.enabled !== 'boolean') return false;
|
||||
if (typeof t.primaryModel !== 'string') return false;
|
||||
if (typeof t.secondaryModel !== 'string') return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function mergeWithDefaults(config: Partial<CliConfig>): CliConfig {
|
||||
const result: CliConfig = {
|
||||
version: config.version ?? DEFAULT_CONFIG.version,
|
||||
tools: { ...DEFAULT_CONFIG.tools }
|
||||
};
|
||||
|
||||
if (config.tools) {
|
||||
for (const toolName of Object.keys(config.tools)) {
|
||||
if (isValidToolName(toolName) && config.tools[toolName]) {
|
||||
result.tools[toolName] = {
|
||||
...DEFAULT_CONFIG.tools[toolName],
|
||||
...config.tools[toolName]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ========== Main Functions ==========
|
||||
|
||||
/**
|
||||
* Load CLI configuration from .workflow/cli-config.json
|
||||
* Returns default config if file doesn't exist or is invalid
|
||||
*/
|
||||
export function loadCliConfig(baseDir: string): CliConfig {
|
||||
const configPath = getConfigPath(baseDir);
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return { ...DEFAULT_CONFIG };
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(configPath, 'utf-8');
|
||||
const parsed = JSON.parse(content);
|
||||
|
||||
if (validateConfig(parsed)) {
|
||||
return mergeWithDefaults(parsed);
|
||||
}
|
||||
|
||||
// Invalid config, return defaults
|
||||
console.warn('[cli-config] Invalid config file, using defaults');
|
||||
return { ...DEFAULT_CONFIG };
|
||||
} catch (err) {
|
||||
console.error('[cli-config] Error loading config:', err);
|
||||
return { ...DEFAULT_CONFIG };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save CLI configuration to .workflow/cli-config.json
|
||||
*/
|
||||
export function saveCliConfig(baseDir: string, config: CliConfig): void {
|
||||
ensureConfigDir(baseDir);
|
||||
const configPath = getConfigPath(baseDir);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||
} catch (err) {
|
||||
console.error('[cli-config] Error saving config:', err);
|
||||
throw new Error(`Failed to save CLI config: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for a specific tool
|
||||
*/
|
||||
export function getToolConfig(baseDir: string, tool: string): CliToolConfig {
|
||||
if (!isValidToolName(tool)) {
|
||||
throw new Error(`Invalid tool name: ${tool}`);
|
||||
}
|
||||
|
||||
const config = loadCliConfig(baseDir);
|
||||
return config.tools[tool] || DEFAULT_CONFIG.tools[tool];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update configuration for a specific tool
|
||||
* Returns the updated tool config
|
||||
*/
|
||||
export function updateToolConfig(
|
||||
baseDir: string,
|
||||
tool: string,
|
||||
updates: Partial<CliToolConfig>
|
||||
): CliToolConfig {
|
||||
if (!isValidToolName(tool)) {
|
||||
throw new Error(`Invalid tool name: ${tool}`);
|
||||
}
|
||||
|
||||
const config = loadCliConfig(baseDir);
|
||||
const currentToolConfig = config.tools[tool] || DEFAULT_CONFIG.tools[tool];
|
||||
|
||||
// Apply updates
|
||||
const updatedToolConfig: CliToolConfig = {
|
||||
enabled: updates.enabled !== undefined ? updates.enabled : currentToolConfig.enabled,
|
||||
primaryModel: updates.primaryModel || currentToolConfig.primaryModel,
|
||||
secondaryModel: updates.secondaryModel || currentToolConfig.secondaryModel
|
||||
};
|
||||
|
||||
// Save updated config
|
||||
config.tools[tool] = updatedToolConfig;
|
||||
saveCliConfig(baseDir, config);
|
||||
|
||||
return updatedToolConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a CLI tool
|
||||
*/
|
||||
export function enableTool(baseDir: string, tool: string): CliToolConfig {
|
||||
return updateToolConfig(baseDir, tool, { enabled: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a CLI tool
|
||||
*/
|
||||
export function disableTool(baseDir: string, tool: string): CliToolConfig {
|
||||
return updateToolConfig(baseDir, tool, { enabled: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tool is enabled
|
||||
*/
|
||||
export function isToolEnabled(baseDir: string, tool: string): boolean {
|
||||
try {
|
||||
const config = getToolConfig(baseDir, tool);
|
||||
return config.enabled;
|
||||
} catch {
|
||||
return true; // Default to enabled if error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary model for a tool
|
||||
*/
|
||||
export function getPrimaryModel(baseDir: string, tool: string): string {
|
||||
try {
|
||||
const config = getToolConfig(baseDir, tool);
|
||||
return config.primaryModel;
|
||||
} catch {
|
||||
return isValidToolName(tool) ? DEFAULT_CONFIG.tools[tool].primaryModel : 'gemini-2.5-pro';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secondary model for a tool (used for internal calls)
|
||||
*/
|
||||
export function getSecondaryModel(baseDir: string, tool: string): string {
|
||||
try {
|
||||
const config = getToolConfig(baseDir, tool);
|
||||
return config.secondaryModel;
|
||||
} catch {
|
||||
return isValidToolName(tool) ? DEFAULT_CONFIG.tools[tool].secondaryModel : 'gemini-2.5-flash';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all predefined models for a tool
|
||||
*/
|
||||
export function getPredefinedModels(tool: string): string[] {
|
||||
if (!isValidToolName(tool)) {
|
||||
return [];
|
||||
}
|
||||
return [...PREDEFINED_MODELS[tool]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full config response for API (includes predefined models)
|
||||
*/
|
||||
export function getFullConfigResponse(baseDir: string): {
|
||||
config: CliConfig;
|
||||
predefinedModels: Record<string, string[]>;
|
||||
} {
|
||||
return {
|
||||
config: loadCliConfig(baseDir),
|
||||
predefinedModels: { ...PREDEFINED_MODELS }
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user