/** * Module import & export verification for VLCode Lite * Tests: import succeeds, exports exist, basic function calls work */ import { fileURLToPath } from 'url'; import path from 'path'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const results = []; let passCount = 0, failCount = 0, warnCount = 0; function ok(mod, msg) { passCount++; results.push(` ✅ ${mod}: ${msg}`); } function fail(mod, msg) { failCount++; results.push(` ❌ ${mod}: ${msg}`); } function warn(mod, msg) { warnCount++; results.push(` ⚠️ ${mod}: ${msg}`); } // ─── 1. Utils ──────────────────────────────────────────────────────── async function testUtils() { console.log('\n═══ utils/ ═══'); try { const { loadConfig, APP_VERSION } = await import('./src/utils/config.js'); if (typeof loadConfig !== 'function') throw new Error('loadConfig not a function'); if (!APP_VERSION) throw new Error('APP_VERSION is empty'); const cfg = loadConfig(['--web', '--port', '9999']); if (cfg.port !== 9999) throw new Error(`port should be 9999, got ${cfg.port}`); if (!['cli', 'api-key'].includes(cfg.llmProvider)) { throw new Error(`llmProvider should be cli or api-key, got ${cfg.llmProvider}`); } ok('config.js', `v${APP_VERSION}, loadConfig() works, port=9999, llmProvider=${cfg.llmProvider}`); } catch (e) { fail('config.js', e.message); } try { const { extractZip, extractZipToDir, createZipFromDir } = await import('./src/utils/zip-extract.js'); if (typeof extractZip !== 'function') throw new Error('extractZip not a function'); if (typeof extractZipToDir !== 'function') throw new Error('extractZipToDir not a function'); if (typeof createZipFromDir !== 'function') throw new Error('createZipFromDir not a function'); const fns = ['extractZip', 'extractZipToDir', 'createZipFromDir']; ok('zip-extract.js', `exports: ${fns.join(', ')}`); } catch (e) { fail('zip-extract.js', e.message); } } // ─── 2. Data ───────────────────────────────────────────────────────── async function testData() { console.log('\n═══ data/ ═══'); try { const { VL_VERSION, THEME_VERSION, PARSEVL_URL } = await import('./src/data/versions.js'); if (!VL_VERSION || !THEME_VERSION || !PARSEVL_URL) throw new Error('missing constants'); ok('versions.js', `VL=${VL_VERSION}, Theme=${THEME_VERSION}`); } catch (e) { fail('versions.js', e.message); } try { const { DOCCENTER_API_URL, DOC_REGISTRY, pathFor, docIdFor, resolveDocRef } = await import('./src/data/doc-paths.js'); if (!DOCCENTER_API_URL) throw new Error('DOCCENTER_API_URL missing'); if (typeof pathFor !== 'function') throw new Error('pathFor not a function'); const p = pathFor('vlSyntax'); if (p !== 1) throw new Error(`pathFor('vlSyntax') should be 1, got ${p}`); ok('doc-paths.js', `API=${DOCCENTER_API_URL.substring(0,30)}..., ${Object.keys(DOC_REGISTRY).length} entries`); } catch (e) { fail('doc-paths.js', e.message); } try { const mod = await import('./src/data/default-theme.js'); ok('default-theme.js', `exports: ${Object.keys(mod).join(', ').substring(0, 60)}`); } catch (e) { fail('default-theme.js', e.message); } } // ─── 3. Core ───────────────────────────────────────────────────────── async function testCore() { console.log('\n═══ core/ ═══'); // tool-registry try { const { ToolRegistry } = await import('./src/core/tool-registry.js'); const reg = new ToolRegistry(); reg.register('TestTool', { description: 'test', parameters: {}, execute: async () => 'ok' }); if (!reg.has('TestTool')) throw new Error('has() failed'); const result = await reg.execute('TestTool', {}); if (!result || result.result !== 'ok') throw new Error(`execute returned ${JSON.stringify(result)}`); const schemas = reg.getToolSchemas(); if (schemas.length !== 1) throw new Error(`schemas.length should be 1, got ${schemas.length}`); reg.clear(); if (reg.listTools().length !== 0) throw new Error('clear() failed'); ok('tool-registry.js', 'register/has/execute/getToolSchemas/clear all work'); } catch (e) { fail('tool-registry.js', e.message); } // context-manager try { const { ContextManager } = await import('./src/core/context-manager.js'); const cm = new ContextManager({ maxOutputTokens: 4096 }); cm.addUserMessage('hello'); cm.addAssistantMessage('hi there'); const msgs = cm.getMessages(); if (msgs.length !== 2) throw new Error(`expected 2 msgs, got ${msgs.length}`); const usage = cm.getUsage(); if (typeof usage.usedTokens !== 'number') throw new Error('getUsage() missing usedTokens'); ok('context-manager.js', `addUser/addAssistant/getMessages/getUsage work, ${msgs.length} msgs, ${usage.usedTokens} tokens`); } catch (e) { fail('context-manager.js', e.message); } // session try { const { SessionManager } = await import('./src/core/session.js'); const sm = new SessionManager('/tmp/vlcode-lite-test'); await sm.init(); const { resumed } = await sm.startOrResume(); ok('session.js', `init/startOrResume work, resumed=${resumed}`); } catch (e) { fail('session.js', e.message); } // session-pool try { const { SessionPool } = await import('./src/core/session-pool.js'); // SessionPool needs shared modules, just verify import ok('session-pool.js', 'import OK (needs shared modules to construct)'); } catch (e) { fail('session-pool.js', e.message); } // llm-provider try { const { CLIProvider, createProvider, isCLIAvailable } = await import('./src/core/llm-provider.js'); if (typeof CLIProvider !== 'function') throw new Error('CLIProvider not a class'); if (typeof createProvider !== 'function') throw new Error('createProvider not a function'); if (typeof isCLIAvailable !== 'function') throw new Error('isCLIAvailable not a function'); const available = await isCLIAvailable(); const provider = createProvider({ model: 'claude-opus-4-6', port: 4002, llmProvider: available ? 'cli' : 'api-key', cliAvailable: available, apiKey: available ? undefined : 'test-key' }); if (!['cli', 'api-key'].includes(provider.name)) { throw new Error(`provider.name should be cli or api-key, got ${provider.name}`); } ok('llm-provider.js', `Provider OK (${provider.name}), claude CLI available=${available}`); } catch (e) { fail('llm-provider.js', e.message); } // prompt-assembler try { const { PromptAssembler } = await import('./src/core/prompt-assembler.js'); const pa = new PromptAssembler( { workDir: '/tmp', model: 'test' }, { isVLProject: () => false, getSummary: () => ({}), getVLConfig: () => '', getProjectMd: () => '', getRules: () => [] } ); const manual = pa.buildInstructionManual(); if (!manual || manual.length === 0) throw new Error('buildInstructionManual returned empty'); ok('prompt-assembler.js', `buildInstructionManual() returns ${manual.length} chars`); } catch (e) { fail('prompt-assembler.js', e.message); } // orchestrator (import only — needs full wiring) try { const { AgentOrchestrator } = await import('./src/core/orchestrator.js'); if (typeof AgentOrchestrator !== 'function') throw new Error('not a class'); ok('orchestrator.js', 'import OK (needs full wiring to construct)'); } catch (e) { fail('orchestrator.js', e.message); } // cli try { const { CLIInterface } = await import('./src/core/cli.js'); if (typeof CLIInterface !== 'function') throw new Error('not a class'); ok('cli.js', 'import OK'); } catch (e) { fail('cli.js', e.message); } // hooks try { const { HooksManager } = await import('./src/core/hooks.js'); const hm = new HooksManager('/tmp/no-hooks'); if (typeof hm.hasHooks !== 'function') throw new Error('hasHooks not a function'); if (hm.hasHooks()) throw new Error('should have no hooks'); ok('hooks.js', 'HooksManager works, hasHooks()=false for empty dir'); } catch (e) { fail('hooks.js', e.message); } } // ─── 4. VL ─────────────────────────────────────────────────────────── async function testVL() { console.log('\n═══ vl/ ═══'); try { const { VLProjectContext } = await import('./src/vl/project-context.js'); const ctx = new VLProjectContext('/tmp/empty-vl-test'); await ctx.load(); if (ctx.isVLProject()) throw new Error('empty dir should not be VL project'); const summary = ctx.getSummary(); if (summary !== null) throw new Error('getSummary should return null for non-VL project'); ok('project-context.js', `load/isVLProject/getSummary work, non-VL returns null`); } catch (e) { fail('project-context.js', e.message); } try { const { VLSymbolIndex } = await import('./src/vl/symbol-index.js'); const { VLProjectContext } = await import('./src/vl/project-context.js'); const ctx = new VLProjectContext('/tmp/empty-vl-test'); await ctx.load(); const si = new VLSymbolIndex(ctx); await si.build(); const stats = si.getStats(); if (typeof stats.totalSymbols !== 'number') throw new Error('missing totalSymbols'); ok('symbol-index.js', `build/getStats work, symbols=${stats.totalSymbols}`); } catch (e) { fail('symbol-index.js', e.message); } try { const { SmartContextLoader } = await import('./src/vl/smart-context.js'); ok('smart-context.js', 'import OK'); } catch (e) { fail('smart-context.js', e.message); } try { const { BlueprintContext } = await import('./src/vl/blueprint-context.js'); const { VLProjectContext } = await import('./src/vl/project-context.js'); const ctx = new VLProjectContext('/tmp/empty-vl-test'); await ctx.load(); const bp = new BlueprintContext(ctx); await bp.load(); if (bp.hasBlueprints()) throw new Error('empty project should have no blueprints'); ok('blueprint-context.js', 'load/hasBlueprints work'); } catch (e) { fail('blueprint-context.js', e.message); } try { const { ImpactAnalyzer } = await import('./src/vl/impact-analyzer.js'); ok('impact-analyzer.js', 'import OK'); } catch (e) { fail('impact-analyzer.js', e.message); } try { const { VLAutoFix } = await import('./src/vl/auto-fix.js'); ok('auto-fix.js', 'import OK'); } catch (e) { fail('auto-fix.js', e.message); } try { const { FileCache } = await import('./src/vl/file-cache.js'); const fc = new FileCache('/tmp'); const stats = fc.getStats(); if (typeof stats.hits !== 'number') throw new Error('getStats missing hits'); ok('file-cache.js', `FileCache works, hitRate=${stats.hitRate}`); } catch (e) { fail('file-cache.js', e.message); } try { const { FileWatcher } = await import('./src/vl/file-watcher.js'); ok('file-watcher.js', 'import OK'); } catch (e) { fail('file-watcher.js', e.message); } try { const mod = await import('./src/vl/metadata-extractor.js'); const fns = Object.keys(mod); ok('metadata-extractor.js', `exports: ${fns.join(', ')}`); } catch (e) { fail('metadata-extractor.js', e.message); } try { const { computeMetaDiff } = await import('./src/vl/meta-diff.js'); if (typeof computeMetaDiff !== 'function') throw new Error('not a function'); const diff = computeMetaDiff({ apps: [], sections: [] }, { apps: [], sections: [] }); if (!diff.summary && diff.summary !== '') throw new Error('missing summary'); ok('meta-diff.js', 'computeMetaDiff() works on empty metas'); } catch (e) { fail('meta-diff.js', e.message); } try { const { parseSections, sectionDiff, mergeSections, smartMerge } = await import('./src/vl/section-diff.js'); const sections = parseSections('# Header\nline1\n# Body\nline2'); if (!(sections instanceof Map)) throw new Error('parseSections should return Map'); if (sections.size < 2) throw new Error(`expected >=2 sections, got ${sections.size}`); const diff = sectionDiff('# A\nfoo', '# A\nbar\n# B\nnew'); if (!diff.modified && !diff.added) throw new Error('diff should detect changes'); ok('section-diff.js', `parseSections=${sections.size} sections, sectionDiff/mergeSections/smartMerge exported`); } catch (e) { fail('section-diff.js', e.message); } try { const { GenerationPipeline } = await import('./src/vl/generation-pipeline.js'); ok('generation-pipeline.js', 'import OK'); } catch (e) { fail('generation-pipeline.js', e.message); } try { const { WorkflowGenerationPipeline } = await import('./src/vl/workflow-generation-pipeline.js'); ok('workflow-generation-pipeline.js', 'import OK'); } catch (e) { fail('workflow-generation-pipeline.js', e.message); } try { const { WorkflowExecutor } = await import('./src/vl/workflow-executor.js'); ok('workflow-executor.js', 'import OK'); } catch (e) { fail('workflow-executor.js', e.message); } try { const { generateTestSkeleton } = await import('./src/vl/meta-test-generator.js'); if (typeof generateTestSkeleton !== 'function') throw new Error('not a function'); const result = generateTestSkeleton({ apps: [], sections: [], serviceDomains: [] }, {}); if (!Array.isArray(result.testCases)) throw new Error('should return testCases array'); ok('meta-test-generator.js', `generateTestSkeleton() returns ${result.testCases.length} cases for empty meta`); } catch (e) { fail('meta-test-generator.js', e.message); } } // ─── 5. Engine ─────────────────────────────────────────────────────── async function testEngine() { console.log('\n═══ engine/ ═══'); try { const { LocalWorkspace } = await import('./src/engine/local-workspace.js'); const lw = new LocalWorkspace('/tmp/vlcode-lite-ws-test'); await lw.createFile({ gid: 'test', path: 'hello.txt', content: 'world' }); const read = await lw.readFile({ gid: 'test', path: 'hello.txt' }); if (!read.content && read.content !== 'world') throw new Error(`readFile failed: ${JSON.stringify(read)}`); await lw.deleteFile({ gid: 'test', path: 'hello.txt' }); ok('local-workspace.js', 'createFile/readFile/deleteFile work'); } catch (e) { fail('local-workspace.js', e.message); } try { const { parseSSEChunk, mapEngineEvent } = await import('./src/engine/sse-parser.js'); if (typeof parseSSEChunk !== 'function') throw new Error('parseSSEChunk not a function'); const events = parseSSEChunk('event: test\ndata: {"foo":"bar"}\n\n'); if (!Array.isArray(events)) throw new Error('should return array'); ok('sse-parser.js', `parseSSEChunk works, parsed ${events.length} events`); } catch (e) { fail('sse-parser.js', e.message); } try { const { WorkflowEngineManager } = await import('./src/engine/workflow-engine-manager.js'); ok('workflow-engine-manager.js', 'import OK'); } catch (e) { fail('workflow-engine-manager.js', e.message); } } // ─── 6. Cloud / MCP ───────────────────────────────────────────────── async function testCloudMcp() { console.log('\n═══ cloud/ + mcp/ ═══'); try { const { CloudAPI } = await import('./src/cloud/cloud-api.js'); const api = new CloudAPI({ cookie: 'test' }); if (typeof api.login !== 'function') throw new Error('login not a function'); if (typeof api.listApps !== 'function') throw new Error('listApps not a function'); if (typeof api.compileWorkspace !== 'function') throw new Error('compileWorkspace not a function'); if (typeof api.syncPush !== 'function') throw new Error('syncPush not a function'); ok('cloud-api.js', 'CloudAPI class OK, all key methods present'); } catch (e) { fail('cloud-api.js', e.message); } // MCP server is a stdio process, just verify import try { // Can't import directly (it starts listening on stdin), just check file exists const fs = await import('fs'); if (!fs.existsSync('./src/mcp/mcp-server.js')) throw new Error('file missing'); ok('mcp-server.js', 'file exists (stdio process, cannot import test)'); } catch (e) { fail('mcp-server.js', e.message); } } // ─── 7. Tools ──────────────────────────────────────────────────────── async function testTools() { console.log('\n═══ tools/ ═══'); const config = { workDir: '/tmp/vlcode-lite-tool-test', model: 'claude-opus-4-6', port: 3300, maxOutputTokens: 32000, llmProvider: 'cli', workspaceApiUrl: 'https://editor.visuallogic.ai/ih5/editor/workspace', autotest: { headless: true, parallelWorkers: 1, maxCases: 3, useWorkflowEngine: true }, }; const toolFiles = [ ['read-file.js', 'createReadFileTool', config], ['edit-file.js', 'createEditFileTool', config], ['write-file.js', 'createWriteFileTool', config], ['bash.js', 'createBashTool', config], ['grep.js', 'createGrepTool', config], ['glob.js', 'createGlobTool', config], ['todo-write.js', 'createTodoWriteTool', null], ['sub-agent.js', 'createSubAgentTool', null], ['ask-user.js', 'createAskUserTool', null], ['tool-search.js', 'createToolSearchTool', null], ['memory.js', 'createMemoryTool', config], ['doc-center.js', 'createDocCenterTool', config], ['component-fetch.js', 'createComponentFetchTool', config], ['browser-inspect.js', 'createBrowserInspectTool', config], ['vl-compile.js', 'createVLCompileTool', config], ['vl-lint.js', 'createVLLintTool', config], ['vl-parse.js', 'createVLParseTool', config], ['vl-syntax-ref.js', 'createVLSyntaxRefTool', config], ['vl-validate.js', 'createVLValidateTool', config], ['vl-metadata.js', 'createVLMetadataTool', config], ['vl-component-test.js', 'createVLComponentTestTool', config], ['vl-generate.js', 'createVLGenerateTool', config], ['vl-adjust.js', 'createVLAdjustTool', config], ['vl-edit-section.js', 'createVLEditSectionTool', config], ['vl-autofix.js', 'createVLAutoFixTool', config], ['vl-cascade-edit.js', 'createVLCascadeEditTool', config], ['vl-symbols.js', 'createVLSymbolsTool', config], ['vl-impact.js', 'createVLImpactTool', config], ['meta-diff.js', 'createMetaDiffTool', config], ['section-diff.js', 'createSectionDiffTool', config], ['vl-meta-test.js', 'createVLMetaTestTool', config], ['workflow-run.js', 'createWorkflowRunTool', config], ['workspace-api.js', 'createWorkspaceAPITool', config], ['autotest-pipeline.js', 'createAutoTestPipelineTool', config], ]; for (const [file, fnName, arg] of toolFiles) { try { const mod = await import(`./src/tools/${file}`); const fn = mod[fnName] || mod.default; if (!fn) { // Check what's actually exported const exports = Object.keys(mod); throw new Error(`${fnName} not found. Exports: [${exports.join(', ')}]`); } if (typeof fn !== 'function') throw new Error(`${fnName} is ${typeof fn}, not function`); let tool; if (arg === null) { tool = fn(); } else if (file === 'workspace-api.js') { const { LocalWorkspace } = await import('./src/engine/local-workspace.js'); tool = fn(config, new LocalWorkspace('/tmp')); } else if (file === 'autotest-pipeline.js') { const { VLProjectContext } = await import('./src/vl/project-context.js'); const ctx = new VLProjectContext('/tmp'); tool = fn(config, ctx); } else { tool = fn(arg); } if (!tool) throw new Error('factory returned null/undefined'); if (!tool.description) throw new Error('missing description'); if (!tool.parameters) throw new Error('missing parameters'); if (typeof tool.execute !== 'function') throw new Error('missing execute function'); ok(file, `${fnName}() → tool with description + parameters + execute`); } catch (e) { fail(file, e.message); } } // Test tool registration integration try { const { ToolRegistry } = await import('./src/core/tool-registry.js'); const { registerAllTools } = await import('./src/tools/index.js'); const { VLProjectContext } = await import('./src/vl/project-context.js'); const { LocalWorkspace } = await import('./src/engine/local-workspace.js'); const reg = new ToolRegistry(); const ctx = new VLProjectContext('/tmp'); await ctx.load(); registerAllTools(reg, config, ctx, { localWorkspace: new LocalWorkspace('/tmp') }); const tools = reg.listTools(); ok('index.js', `registerAllTools() registered ${tools.length} tools: ${tools.join(', ')}`); } catch (e) { fail('index.js (registerAllTools)', e.message); } } // ─── 8. Server ─────────────────────────────────────────────────────── async function testServer() { console.log('\n═══ server/ ═══'); try { const { WebServer } = await import('./src/server/server.js'); if (typeof WebServer !== 'function') throw new Error('not a class'); ok('server.js', 'WebServer class import OK'); } catch (e) { fail('server.js', e.message); } try { const mod = await import('./src/server/sse.js'); if (typeof mod.setupSSE !== 'function') throw new Error('setupSSE not a function'); ok('sse.js', 'setupSSE function OK'); } catch (e) { fail('sse.js', e.message); } try { const mod = await import('./src/server/helpers.js'); const fns = ['getCookie', 'clearCookie', 'saveCookie', 'loadWorkspaces', 'saveWorkspaces', 'ensureVLBible', 'hasVLFiles', 'ensureProjectProfile', 'ensureVLCodeHome']; const missing = fns.filter(f => typeof mod[f] !== 'function'); if (missing.length > 0) throw new Error(`missing functions: ${missing.join(', ')}`); ok('helpers.js', `all ${fns.length} helper functions exported`); } catch (e) { fail('helpers.js', e.message); } try { const { WebServer } = await import('./src/server/index.js'); if (typeof WebServer !== 'function') throw new Error('re-export failed'); ok('index.js', 'WebServer re-export OK'); } catch (e) { fail('server/index.js', e.message); } // Route modules — verify they export setup functions const routes = [ 'chat.js', 'files.js', 'project.js', 'workspace.js', 'compile.js', 'workflow.js', 'cloud.js', 'intelligence.js', 'tools.js', 'conversation.js', 'misc.js' ]; const setupFnNames = { 'chat.js': 'setupChatRoutes', 'files.js': 'setupFileRoutes', 'project.js': 'setupProjectRoutes', 'workspace.js': 'setupWorkspaceRoutes', 'compile.js': 'setupCompileRoutes', 'workflow.js': 'setupWorkflowRoutes', 'cloud.js': 'setupCloudRoutes', 'intelligence.js': 'setupIntelligenceRoutes', 'tools.js': 'setupToolRoutes', 'conversation.js': 'setupConversationRoutes', 'misc.js': 'setupMiscRoutes', }; for (const route of routes) { try { const mod = await import(`./src/server/routes/${route}`); const expectedFn = setupFnNames[route]; if (expectedFn && typeof mod[expectedFn] !== 'function') { throw new Error(`${expectedFn} not found. Exports: [${Object.keys(mod).join(', ')}]`); } ok(`routes/${route}`, `${expectedFn}() exported`); } catch (e) { fail(`routes/${route}`, e.message); } } } // ─── 9. Autotest sub-modules ───────────────────────────────────────── async function testAutotest() { console.log('\n═══ tools/autotest/ ═══'); const files = [ 'test-generator.js', 'test-runner.js', 'test-evaluator.js', 'auto-fixer.js', 'case-parser.js', 'report-generator.js', 'workflow-builder.js', 'workflow-runner.js' ]; for (const file of files) { try { const mod = await import(`./src/tools/autotest/${file}`); const exports = Object.keys(mod); if (exports.length === 0) throw new Error('no exports'); ok(`autotest/${file}`, `exports: ${exports.join(', ')}`); } catch (e) { fail(`autotest/${file}`, e.message); } } } // ─── 10. Functional tests (deeper) ────────────────────────────────── async function testFunctional() { console.log('\n═══ Functional Tests ═══'); // ReadFile tool — actually read a file try { const { createReadFileTool } = await import('./src/tools/read-file.js'); const tool = createReadFileTool({ workDir: '/tmp' }); const result = await tool.execute({ file_path: path.join(__dirname, 'package.json') }); const text = result.result || result; if (!text.includes('vlcode-lite')) throw new Error('ReadFile did not return package.json content'); ok('ReadFile.execute', 'successfully reads package.json'); } catch (e) { fail('ReadFile.execute', e.message); } // WriteFile + EditFile + ReadFile round-trip try { const { createWriteFileTool } = await import('./src/tools/write-file.js'); const { createEditFileTool } = await import('./src/tools/edit-file.js'); const { createReadFileTool } = await import('./src/tools/read-file.js'); const cfg = { workDir: '/tmp' }; const testFile = '/tmp/vlcode-lite-test-roundtrip.txt'; const wt = createWriteFileTool(cfg); await wt.execute({ file_path: testFile, content: 'hello world' }); const et = createEditFileTool(cfg); await et.execute({ file_path: testFile, old_string: 'hello', new_string: 'goodbye' }); const rt = createReadFileTool(cfg); const readResult = await rt.execute({ file_path: testFile }); const content = readResult.result || readResult; if (!content.includes('goodbye world')) throw new Error(`expected 'goodbye world', got: ${String(content).substring(0, 100)}`); ok('Write→Edit→Read roundtrip', 'file operations work correctly'); } catch (e) { fail('Write→Edit→Read roundtrip', e.message); } // Bash tool try { const { createBashTool } = await import('./src/tools/bash.js'); const tool = createBashTool({ workDir: '/tmp' }); const result = await tool.execute({ command: 'echo "vlcode-lite-test"' }); if (!result.includes('vlcode-lite-test')) throw new Error(`expected echo output, got: ${result}`); ok('Bash.execute', 'echo command works'); } catch (e) { fail('Bash.execute', e.message); } // Grep tool try { const { createGrepTool } = await import('./src/tools/grep.js'); const tool = createGrepTool({ workDir: __dirname }); const result = await tool.execute({ pattern: 'vlcode-lite', path: path.join(__dirname, 'package.json') }); if (!result) throw new Error('grep returned empty'); ok('Grep.execute', 'finds "vlcode-lite" in package.json'); } catch (e) { fail('Grep.execute', e.message); } // Glob tool try { const { createGlobTool } = await import('./src/tools/glob.js'); const tool = createGlobTool({ workDir: __dirname }); const result = await tool.execute({ pattern: 'src/core/*.js', path: __dirname }); if (!result.includes('orchestrator.js')) throw new Error(`glob did not find orchestrator.js: ${result.substring(0, 200)}`); ok('Glob.execute', 'finds src/core/*.js files'); } catch (e) { fail('Glob.execute', e.message); } // Memory tool try { const { createMemoryTool } = await import('./src/tools/memory.js'); const tool = createMemoryTool({ workDir: '/tmp/vlcode-lite-mem-test' }); await tool.execute({ operation: 'write', scope: 'project', key: 'test-key', value: 'test-value-123' }); const readResult = await tool.execute({ operation: 'read', scope: 'project', key: 'test-key' }); if (!readResult.includes('test-value-123')) throw new Error(`memory read failed: ${readResult}`); await tool.execute({ operation: 'delete', scope: 'project', key: 'test-key' }); ok('Memory.execute', 'write/read/delete cycle works'); } catch (e) { fail('Memory.execute', e.message); } // LocalWorkspace advanced try { const { LocalWorkspace } = await import('./src/engine/local-workspace.js'); const lw = new LocalWorkspace('/tmp/vlcode-lite-lw-test'); await lw.writeFiles({ gid: 'test', files: [ { path: 'a.txt', content: 'aaa' }, { path: 'sub/b.txt', content: 'bbb' }, ]}); const list = await lw.listFile({ gid: 'test' }); if (!list.files || list.files.length < 2) throw new Error(`listFile should find >=2 files, got ${JSON.stringify(list)}`); const reads = await lw.readFiles({ gid: 'test', paths: ['a.txt', 'sub/b.txt'] }); if (!reads.files || reads.files.length !== 2) throw new Error(`readFiles should return 2, got ${JSON.stringify(reads)}`); ok('LocalWorkspace batch', 'writeFiles/listFile/readFiles work'); } catch (e) { fail('LocalWorkspace batch', e.message); } } // ─── Run all ───────────────────────────────────────────────────────── async function main() { console.log('╔══════════════════════════════════════════════════╗'); console.log('║ VLCode Lite — Module Verification Suite ║'); console.log('╚══════════════════════════════════════════════════╝'); await testUtils(); await testData(); await testCore(); await testVL(); await testEngine(); await testCloudMcp(); await testTools(); await testServer(); await testAutotest(); await testFunctional(); console.log('\n══════════════════════════════════════════════════'); console.log(` Results: ${passCount} passed, ${failCount} failed, ${warnCount} warnings`); console.log('══════════════════════════════════════════════════'); if (failCount > 0) { console.log('\n❌ FAILURES:'); results.filter(r => r.includes('❌')).forEach(r => console.log(r)); } if (warnCount > 0) { console.log('\n⚠️ WARNINGS:'); results.filter(r => r.includes('⚠️')).forEach(r => console.log(r)); } console.log('\n✅ PASSED:'); results.filter(r => r.includes('✅')).forEach(r => console.log(r)); process.exit(failCount > 0 ? 1 : 0); } main().catch(e => { console.error('FATAL:', e); process.exit(2); });