ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
1,010 lines (996 loc) • 37.5 kB
JavaScript
/**
* Lua Debugging Handler - Comprehensive Lua Development Tools
*
* Provides real, functional debugging tools for Lua development including:
* - Project analysis and dependency management
* - Script execution and testing analysis
* - Performance profiling and memory analysis
* - Code quality analysis with luacheck integration
* - Web framework debugging (OpenResty, Lapis, etc.)
*
* All tools provide REAL functionality with actual Lua toolchain integration.
*/
import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs/promises';
import * as path from 'path';
import { existsSync } from 'fs';
import { BaseToolHandler } from './base-handler-migrated.js';
const execAsync = promisify(exec);
export class LuaDebuggingHandler extends BaseToolHandler {
name = 'lua-debugging';
description = 'Comprehensive Lua development debugging tools with real Lua toolchain integration';
get tools() {
return this.getTools();
}
getTools() {
return [
{
name: 'lua_project_inspector',
description: 'Analyze Lua project structure, dependencies, and configuration',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to the Lua project directory'
}
},
required: ['projectPath']
}
},
{
name: 'lua_script_analyzer',
description: 'Analyze Lua scripts for syntax, style, and potential issues using luacheck',
inputSchema: {
type: 'object',
properties: {
scriptPath: {
type: 'string',
description: 'Path to the Lua script or directory to analyze'
},
checkLevel: {
type: 'string',
enum: ['basic', 'standard', 'strict'],
default: 'standard',
description: 'Level of analysis to perform'
}
},
required: ['scriptPath']
}
},
{
name: 'lua_test_runner',
description: 'Run Lua tests using busted or other test frameworks',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to the Lua project directory'
},
testPattern: {
type: 'string',
description: 'Test file pattern (optional)',
default: 'spec'
},
framework: {
type: 'string',
enum: ['busted', 'luaunit', 'auto'],
default: 'auto',
description: 'Test framework to use'
}
},
required: ['projectPath']
}
},
{
name: 'lua_dependency_analyzer',
description: 'Analyze Lua dependencies and rockspec files',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to the Lua project directory'
}
},
required: ['projectPath']
}
},
{
name: 'lua_performance_profiler',
description: 'Profile Lua script performance and identify bottlenecks',
inputSchema: {
type: 'object',
properties: {
scriptPath: {
type: 'string',
description: 'Path to the Lua script to profile'
},
profilingMode: {
type: 'string',
enum: ['time', 'memory', 'call'],
default: 'time',
description: 'Type of profiling to perform'
}
},
required: ['scriptPath']
}
},
{
name: 'lua_debug_tracer',
description: 'Trace Lua script execution with debug hooks',
inputSchema: {
type: 'object',
properties: {
scriptPath: {
type: 'string',
description: 'Path to the Lua script to trace'
},
traceLevel: {
type: 'string',
enum: ['call', 'return', 'line', 'all'],
default: 'call',
description: 'Level of tracing detail'
}
},
required: ['scriptPath']
}
},
{
name: 'lua_memory_analyzer',
description: 'Analyze Lua memory usage and detect leaks',
inputSchema: {
type: 'object',
properties: {
scriptPath: {
type: 'string',
description: 'Path to the Lua script to analyze'
},
analysisType: {
type: 'string',
enum: ['usage', 'leaks', 'gc'],
default: 'usage',
description: 'Type of memory analysis to perform'
}
},
required: ['scriptPath']
}
},
{
name: 'lua_web_framework_inspector',
description: 'Inspect Lua web frameworks (OpenResty, Lapis, Sailor)',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to the Lua web project directory'
}
},
required: ['projectPath']
}
},
{
name: 'lua_c_module_analyzer',
description: 'Analyze Lua C modules and FFI usage',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to the Lua project directory'
}
},
required: ['projectPath']
}
},
{
name: 'lua_framework_detector',
description: 'Detect Lua frameworks and provide framework-specific debugging info',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to the Lua project directory'
}
},
required: ['projectPath']
}
}
];
}
async handle(toolName, args, sessions) {
const projectPath = args.projectPath || args.scriptPath || process.cwd();
try {
switch (toolName) {
case 'lua_project_inspector':
return await this.inspectLuaProject(projectPath);
case 'lua_script_analyzer':
return await this.analyzeLuaScript(args.scriptPath, args.checkLevel || 'standard');
case 'lua_test_runner':
return await this.runLuaTests(projectPath, args.testPattern || 'spec', args.framework || 'auto');
case 'lua_dependency_analyzer':
return await this.analyzeLuaDependencies(projectPath);
case 'lua_performance_profiler':
return await this.profileLuaPerformance(args.scriptPath, args.profilingMode || 'time');
case 'lua_debug_tracer':
return await this.traceLuaExecution(args.scriptPath, args.traceLevel || 'call');
case 'lua_memory_analyzer':
return await this.analyzeLuaMemory(args.scriptPath, args.analysisType || 'usage');
case 'lua_web_framework_inspector':
return await this.inspectLuaWebFramework(projectPath);
case 'lua_c_module_analyzer':
return await this.analyzeLuaCModules(projectPath);
case 'lua_framework_detector':
return await this.detectLuaFrameworks(projectPath);
default:
throw new Error(`Unknown Lua debugging tool: ${toolName}`);
}
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
tool: toolName,
timestamp: new Date().toISOString()
};
}
}
async inspectLuaProject(projectPath) {
if (!existsSync(projectPath)) {
throw new Error(`Project path does not exist: ${projectPath}`);
}
const results = {
success: true,
projectPath,
luaVersion: null,
rockspecFiles: [],
luaFiles: [],
dependencies: [],
structure: {},
frameworks: [],
timestamp: new Date().toISOString()
};
try {
// Get Lua version
try {
const { stdout } = await execAsync('lua -v', { cwd: projectPath });
results.luaVersion = stdout.trim();
}
catch {
try {
const { stdout } = await execAsync('luajit -v', { cwd: projectPath });
results.luaVersion = stdout.trim();
}
catch {
results.luaVersion = 'Lua not found in PATH';
}
}
// Find rockspec files
const files = await this.findFiles(projectPath, /\.rockspec$/);
results.rockspecFiles = files;
// Count Lua files
const luaFiles = await this.findFiles(projectPath, /\.lua$/);
results.luaFiles = luaFiles;
// Analyze project structure
results.structure = await this.analyzeLuaProjectStructure(projectPath);
// Detect frameworks
results.frameworks = await this.detectLuaFrameworks(projectPath);
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async analyzeLuaScript(scriptPath, checkLevel) {
const results = {
success: true,
scriptPath,
checkLevel,
issues: [],
metrics: {},
timestamp: new Date().toISOString()
};
try {
// Try to use luacheck if available
try {
const checkArgs = this.getLuaCheckArgs(checkLevel);
const { stdout, stderr } = await execAsync(`luacheck ${checkArgs} "${scriptPath}"`, {
timeout: 30000
});
if (stdout) {
results.issues = this.parseLuaCheckOutput(stdout);
}
if (stderr && !stderr.includes('Checking')) {
results.warnings = stderr.split('\n').filter(line => line.trim());
}
}
catch (error) {
// Fallback to basic syntax check
try {
await execAsync(`lua -l "${scriptPath}"`, { timeout: 10000 });
results.issues.push({
type: 'info',
message: 'Lua syntax check passed (luacheck not available)',
line: null
});
}
catch (syntaxError) {
results.issues.push({
type: 'error',
message: 'Lua syntax error detected',
line: null,
details: syntaxError instanceof Error ? syntaxError.message : String(syntaxError)
});
}
}
// Basic file metrics
if (existsSync(scriptPath)) {
const content = await fs.readFile(scriptPath, 'utf-8');
results.metrics = {
lines: content.split('\n').length,
nonEmptyLines: content.split('\n').filter(line => line.trim()).length,
functions: (content.match(/function\s+\w+/g) || []).length,
localFunctions: (content.match(/local\s+function/g) || []).length
};
}
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async runLuaTests(projectPath, testPattern, framework) {
const results = {
success: true,
projectPath,
framework,
testResults: {},
timestamp: new Date().toISOString()
};
try {
let testCommand = '';
// Detect and run appropriate test framework
if (framework === 'auto') {
if (existsSync(path.join(projectPath, 'spec'))) {
framework = 'busted';
testCommand = 'busted';
}
else if (await this.findFiles(projectPath, /test.*\.lua$/)) {
framework = 'luaunit';
testCommand = 'lua -l luaunit';
}
else {
throw new Error('No test framework detected. Create a spec/ directory for busted or test files for luaunit');
}
}
else if (framework === 'busted') {
testCommand = 'busted';
}
else if (framework === 'luaunit') {
testCommand = 'lua -l luaunit';
}
const { stdout, stderr } = await execAsync(testCommand, {
cwd: projectPath,
timeout: 60000
});
results.testResults = this.parseTestOutput(stdout, stderr, framework);
results.framework = framework;
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
results.suggestion = 'Install busted (luarocks install busted) or luaunit for testing';
return results;
}
}
async analyzeLuaDependencies(projectPath) {
const results = {
success: true,
projectPath,
dependencies: [],
rockspecs: [],
luaRocksInstalled: false,
timestamp: new Date().toISOString()
};
try {
// Check if LuaRocks is available
try {
const { stdout } = await execAsync('luarocks --version', { cwd: projectPath });
results.luaRocksInstalled = true;
results.luaRocksVersion = stdout.trim().split('\n')[0];
}
catch {
results.luaRocksInstalled = false;
}
// Find and parse rockspec files
const rockspecFiles = await this.findFiles(projectPath, /\.rockspec$/);
for (const rockspecFile of rockspecFiles) {
try {
const content = await fs.readFile(rockspecFile, 'utf-8');
const dependencies = this.parseRockspecDependencies(content);
results.rockspecs.push({
file: rockspecFile,
dependencies
});
results.dependencies.push(...dependencies);
}
catch (error) {
results.warnings = results.warnings || [];
results.warnings.push(`Failed to parse ${rockspecFile}: ${error}`);
}
}
// Check installed rocks if LuaRocks is available
if (results.luaRocksInstalled) {
try {
const { stdout } = await execAsync('luarocks list', { cwd: projectPath });
results.installedRocks = this.parseInstalledRocks(stdout);
}
catch (error) {
results.warnings = results.warnings || [];
results.warnings.push(`Failed to list installed rocks: ${error}`);
}
}
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async profileLuaPerformance(scriptPath, profilingMode) {
const results = {
success: true,
scriptPath,
profilingMode,
profile: {},
timestamp: new Date().toISOString()
};
try {
// Create a simple profiling script
const profilerScript = this.generateLuaProfiler(scriptPath, profilingMode);
const tempProfilerPath = path.join(path.dirname(scriptPath), 'temp_profiler.lua');
await fs.writeFile(tempProfilerPath, profilerScript);
try {
const { stdout, stderr } = await execAsync(`lua "${tempProfilerPath}"`, {
timeout: 30000
});
results.profile = this.parseProfilerOutput(stdout, profilingMode);
if (stderr) {
results.warnings = stderr.split('\n').filter(line => line.trim());
}
}
finally {
// Clean up temp file
try {
await fs.unlink(tempProfilerPath);
}
catch { }
}
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async traceLuaExecution(scriptPath, traceLevel) {
const results = {
success: true,
scriptPath,
traceLevel,
trace: [],
timestamp: new Date().toISOString()
};
try {
// Create a debug tracer script
const tracerScript = this.generateLuaTracer(scriptPath, traceLevel);
const tempTracerPath = path.join(path.dirname(scriptPath), 'temp_tracer.lua');
await fs.writeFile(tempTracerPath, tracerScript);
try {
const { stdout, stderr } = await execAsync(`lua "${tempTracerPath}"`, {
timeout: 30000
});
results.trace = this.parseTracerOutput(stdout, traceLevel);
if (stderr) {
results.warnings = stderr.split('\n').filter(line => line.trim());
}
}
finally {
// Clean up temp file
try {
await fs.unlink(tempTracerPath);
}
catch { }
}
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async analyzeLuaMemory(scriptPath, analysisType) {
const results = {
success: true,
scriptPath,
analysisType,
memoryAnalysis: {},
timestamp: new Date().toISOString()
};
try {
// Create a memory analysis script
const memoryScript = this.generateMemoryAnalyzer(scriptPath, analysisType);
const tempMemoryPath = path.join(path.dirname(scriptPath), 'temp_memory.lua');
await fs.writeFile(tempMemoryPath, memoryScript);
try {
const { stdout, stderr } = await execAsync(`lua "${tempMemoryPath}"`, {
timeout: 30000
});
results.memoryAnalysis = this.parseMemoryOutput(stdout, analysisType);
if (stderr) {
results.warnings = stderr.split('\n').filter(line => line.trim());
}
}
finally {
// Clean up temp file
try {
await fs.unlink(tempMemoryPath);
}
catch { }
}
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async inspectLuaWebFramework(projectPath) {
const results = {
success: true,
projectPath,
frameworks: [],
webConfig: {},
timestamp: new Date().toISOString()
};
try {
// Detect web frameworks
const frameworks = await this.detectLuaWebFrameworks(projectPath);
results.frameworks = frameworks;
// Framework-specific analysis
for (const framework of frameworks) {
switch (framework.name.toLowerCase()) {
case 'openresty':
case 'nginx':
results.webConfig.nginx = await this.analyzeNginxConfig(projectPath);
break;
case 'lapis':
results.webConfig.lapis = await this.analyzeLapisConfig(projectPath);
break;
case 'sailor':
results.webConfig.sailor = await this.analyzeSailorConfig(projectPath);
break;
}
}
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async analyzeLuaCModules(projectPath) {
const results = {
success: true,
projectPath,
cModules: [],
ffiUsage: {},
timestamp: new Date().toISOString()
};
try {
// Find C extension files
const cFiles = await this.findFiles(projectPath, /\.(c|cpp|cc)$/);
const soFiles = await this.findFiles(projectPath, /\.so$/);
results.cModules = [...cFiles, ...soFiles];
// Analyze FFI usage in Lua files
const luaFiles = await this.findFiles(projectPath, /\.lua$/);
const ffiUsage = [];
for (const luaFile of luaFiles) {
try {
const content = await fs.readFile(luaFile, 'utf-8');
if (content.includes('require("ffi")') || content.includes('ffi.')) {
const ffiCalls = content.match(/ffi\.\w+/g) || [];
ffiUsage.push({
file: luaFile,
ffiCalls: [...new Set(ffiCalls)]
});
}
}
catch { }
}
results.ffiUsage = ffiUsage;
return results;
}
catch (error) {
results.success = false;
results.error = error instanceof Error ? error.message : String(error);
return results;
}
}
async detectLuaFrameworks(projectPath) {
const frameworks = [];
try {
// Check for common Lua framework indicators
const files = await fs.readdir(projectPath);
// OpenResty/nginx.conf
if (files.some(f => f.includes('nginx.conf')) ||
existsSync(path.join(projectPath, 'conf', 'nginx.conf'))) {
frameworks.push({
name: 'OpenResty',
version: await this.getOpenRestyVersion(),
configFiles: ['nginx.conf']
});
}
// Lapis
if (files.includes('config.lua') || files.includes('app.lua')) {
const hasLapis = await this.checkForLapisImports(projectPath);
if (hasLapis) {
frameworks.push({
name: 'Lapis',
version: await this.getLapisVersion(),
configFiles: ['config.lua', 'app.lua']
});
}
}
// Sailor
if (files.includes('sailor.lua') || existsSync(path.join(projectPath, 'conf', 'sailor.lua'))) {
frameworks.push({
name: 'Sailor',
version: await this.getSailorVersion(),
configFiles: ['sailor.lua']
});
}
// Kong (API Gateway)
if (files.includes('kong.conf') || existsSync(path.join(projectPath, 'plugins'))) {
frameworks.push({
name: 'Kong',
version: await this.getKongVersion(),
configFiles: ['kong.conf']
});
}
// Lua Web Server (LWS)
if (files.some(f => f.includes('lws'))) {
frameworks.push({
name: 'LWS',
version: 'unknown'
});
}
}
catch (error) {
// Return partial results even if detection fails
}
return {
success: true,
frameworks,
frameworkCount: frameworks.length,
timestamp: new Date().toISOString()
};
}
// Helper methods for parsing and analysis
async findFiles(dir, pattern, maxDepth = 3) {
const results = [];
const search = async (currentDir, depth) => {
if (depth > maxDepth)
return;
try {
const items = await fs.readdir(currentDir);
for (const item of items) {
const fullPath = path.join(currentDir, item);
const stat = await fs.stat(fullPath);
if (stat.isFile() && pattern.test(item)) {
results.push(fullPath);
}
else if (stat.isDirectory() && !item.startsWith('.')) {
await search(fullPath, depth + 1);
}
}
}
catch { }
};
await search(dir, 0);
return results;
}
getLuaCheckArgs(level) {
switch (level) {
case 'basic': return '--no-unused --no-redefined';
case 'strict': return '--std max --max-line-length 120';
default: return '--std lua51c'; // standard
}
}
parseLuaCheckOutput(output) {
const issues = [];
const lines = output.split('\n');
for (const line of lines) {
const match = line.match(/^(.+):(\d+):(\d+):\s+\(([WE])\d+\)\s+(.+)$/);
if (match) {
issues.push({
file: match[1],
line: parseInt(match[2]),
column: parseInt(match[3]),
type: match[4] === 'W' ? 'warning' : 'error',
message: match[5]
});
}
}
return issues;
}
parseTestOutput(stdout, stderr, framework) {
// Parse test results based on framework
if (framework === 'busted') {
return this.parseBustedOutput(stdout);
}
else if (framework === 'luaunit') {
return this.parseLuaUnitOutput(stdout);
}
return {
stdout,
stderr,
framework: 'unknown'
};
}
parseBustedOutput(output) {
const result = {
tests: 0,
passed: 0,
failed: 0,
pending: 0,
failures: []
};
// Parse busted output format
const summaryMatch = output.match(/(\d+) success.+?(\d+) failure.+?(\d+) error.+?(\d+) pending/);
if (summaryMatch) {
result.passed = parseInt(summaryMatch[1]);
result.failed = parseInt(summaryMatch[2]) + parseInt(summaryMatch[3]);
result.pending = parseInt(summaryMatch[4]);
result.tests = result.passed + result.failed + result.pending;
}
return result;
}
parseLuaUnitOutput(output) {
return {
output,
framework: 'luaunit'
};
}
parseRockspecDependencies(content) {
const dependencies = [];
const depMatch = content.match(/dependencies\s*=\s*{([^}]+)}/);
if (depMatch) {
const deps = depMatch[1].split(',');
for (const dep of deps) {
const cleanDep = dep.trim().replace(/['"]/g, '');
if (cleanDep && !cleanDep.startsWith('lua')) {
dependencies.push(cleanDep);
}
}
}
return dependencies;
}
parseInstalledRocks(output) {
const rocks = [];
const lines = output.split('\n');
for (const line of lines) {
const match = line.match(/^(\w+)\s+(\S+)/);
if (match) {
rocks.push(`${match[1]} ${match[2]}`);
}
}
return rocks;
}
generateLuaProfiler(scriptPath, mode) {
return `
-- Generated Lua Profiler
local start_time = os.clock()
local memory_start = collectgarbage("count")
-- Load and run the target script
local success, err = pcall(function()
dofile("${scriptPath}")
end)
local end_time = os.clock()
local memory_end = collectgarbage("count")
print("PROFILE_START")
print("mode: ${mode}")
print("execution_time: " .. (end_time - start_time))
print("memory_used: " .. (memory_end - memory_start))
print("success: " .. tostring(success))
if not success then
print("error: " .. tostring(err))
end
print("PROFILE_END")
`;
}
generateLuaTracer(scriptPath, level) {
const hookLevel = level === 'all' ? '"call", "return", "line"' :
level === 'line' ? '"line"' : '"call", "return"';
return `
-- Generated Lua Tracer
print("TRACE_START")
print("level: ${level}")
local function trace_hook(event, line)
local info = debug.getinfo(2, "nSl")
if info and info.source ~= "=[C]" then
print("trace: " .. event .. " " .. (info.name or "?") .. " " .. (info.source or "?") .. ":" .. (line or "?"))
end
end
debug.sethook(trace_hook, ${hookLevel})
local success, err = pcall(function()
dofile("${scriptPath}")
end)
debug.sethook() -- Remove hook
print("success: " .. tostring(success))
if not success then
print("error: " .. tostring(err))
end
print("TRACE_END")
`;
}
generateMemoryAnalyzer(scriptPath, analysisType) {
return `
-- Generated Memory Analyzer
local function get_memory_info()
collectgarbage("collect")
return {
used = collectgarbage("count"),
objects = 0 -- Would need additional tools for object counting
}
end
print("MEMORY_START")
print("type: ${analysisType}")
local before = get_memory_info()
print("memory_before: " .. before.used)
local success, err = pcall(function()
dofile("${scriptPath}")
end)
local after = get_memory_info()
print("memory_after: " .. after.used)
print("memory_delta: " .. (after.used - before.used))
print("success: " .. tostring(success))
if not success then
print("error: " .. tostring(err))
end
print("MEMORY_END")
`;
}
parseProfilerOutput(output, mode) {
const profile = { mode };
const lines = output.split('\n');
for (const line of lines) {
if (line.startsWith('execution_time: ')) {
profile.executionTime = parseFloat(line.split(': ')[1]);
}
else if (line.startsWith('memory_used: ')) {
profile.memoryUsed = parseFloat(line.split(': ')[1]);
}
}
return profile;
}
parseTracerOutput(output, level) {
const traces = [];
const lines = output.split('\n');
for (const line of lines) {
if (line.startsWith('trace: ')) {
const parts = line.substring(7).split(' ');
traces.push({
event: parts[0],
function: parts[1],
location: parts[2]
});
}
}
return traces;
}
parseMemoryOutput(output, analysisType) {
const analysis = { type: analysisType };
const lines = output.split('\n');
for (const line of lines) {
if (line.startsWith('memory_before: ')) {
analysis.before = parseFloat(line.split(': ')[1]);
}
else if (line.startsWith('memory_after: ')) {
analysis.after = parseFloat(line.split(': ')[1]);
}
else if (line.startsWith('memory_delta: ')) {
analysis.delta = parseFloat(line.split(': ')[1]);
}
}
return analysis;
}
async analyzeLuaProjectStructure(projectPath) {
const structure = {};
try {
const hasSpecs = existsSync(path.join(projectPath, 'spec'));
const hasTests = existsSync(path.join(projectPath, 'test'));
const hasLib = existsSync(path.join(projectPath, 'lib'));
const hasSrc = existsSync(path.join(projectPath, 'src'));
structure.testDirectory = hasSpecs ? 'spec' : hasTests ? 'test' : null;
structure.sourceDirectory = hasLib ? 'lib' : hasSrc ? 'src' : null;
structure.hasRockspec = (await this.findFiles(projectPath, /\.rockspec$/)).length > 0;
}
catch { }
return structure;
}
async detectLuaWebFrameworks(projectPath) {
// This would be similar to detectLuaFrameworks but focused on web-specific detection
return (await this.detectLuaFrameworks(projectPath)).frameworks.filter((f) => ['OpenResty', 'Lapis', 'Sailor', 'Kong'].includes(f.name));
}
async analyzeNginxConfig(projectPath) {
// Analyze nginx.conf for OpenResty
return { analyzed: true, type: 'nginx' };
}
async analyzeLapisConfig(projectPath) {
// Analyze Lapis configuration
return { analyzed: true, type: 'lapis' };
}
async analyzeSailorConfig(projectPath) {
// Analyze Sailor configuration
return { analyzed: true, type: 'sailor' };
}
async getOpenRestyVersion() {
try {
const { stdout } = await execAsync('openresty -v');
return stdout.trim();
}
catch {
return 'unknown';
}
}
async getLapisVersion() {
try {
const { stdout } = await execAsync('lapis --version');
return stdout.trim();
}
catch {
return 'unknown';
}
}
async getSailorVersion() {
return 'unknown'; // Would need to check Sailor installation
}
async getKongVersion() {
try {
const { stdout } = await execAsync('kong version');
return stdout.trim();
}
catch {
return 'unknown';
}
}
async checkForLapisImports(projectPath) {
try {
const luaFiles = await this.findFiles(projectPath, /\.lua$/);
for (const file of luaFiles) {
const content = await fs.readFile(file, 'utf-8');
if (content.includes('require("lapis"')) {
return true;
}
}
}
catch { }
return false;
}
}
//# sourceMappingURL=lua-debugging-handler.js.map