meld
Version:
Meld: A template language for LLM prompts
249 lines (206 loc) • 9.96 kB
text/typescript
import { container } from 'tsyringe';
import { IResolutionService } from '@services/resolution/ResolutionService/IResolutionService.js';
import { IStateService } from '@services/state/StateService/IStateService.js';
import { IParserService } from '@services/pipeline/ParserService/IParserService.js';
import { IInterpreterService } from '@services/pipeline/InterpreterService/IInterpreterService.js';
import { IFileSystemService } from '@services/fs/FileSystemService/IFileSystemService.js';
import { MeldResolutionError } from '@core/errors/MeldResolutionError.js';
import path from 'path';
import chalk from 'chalk';
import { VariableResolutionTracker, ResolutionTrackingConfig } from '../../tests/utils/debug/index.js';
import { IPathService } from '@services/fs/PathService/IPathService.js';
// Import concrete classes for direct instantiation
import { ResolutionService } from '@services/resolution/ResolutionService/ResolutionService.js';
import { StateService } from '@services/state/StateService/StateService.js';
import { ParserService } from '@services/pipeline/ParserService/ParserService.js';
import { InterpreterService } from '@services/pipeline/InterpreterService/InterpreterService.js';
import { FileSystemService } from '@services/fs/FileSystemService/FileSystemService.js';
import { PathService } from '@services/fs/PathService/PathService.js';
import { NodeFileSystem } from '@services/fs/FileSystemService/NodeFileSystem.js';
import { PathOperationsService } from '@services/fs/FileSystemService/PathOperationsService.js';
import { DirectiveService } from '@services/pipeline/DirectiveService/DirectiveService.js';
import { ValidationService } from '@services/resolution/ValidationService/ValidationService.js';
import { CircularityService } from '@services/resolution/CircularityService/CircularityService.js';
interface DebugResolutionOptions {
filePath: string;
variableName?: string;
watchMode?: boolean;
outputFormat?: 'json' | 'text';
}
// Define interfaces for the resolution tracker types
interface ResolutionAttempt {
variableName: string;
context: string;
timestamp: number;
success: boolean;
value?: any;
source?: string;
contextBoundary?: {
type: 'parent-to-child' | 'child-to-parent';
sourceId?: string;
targetId?: string;
};
}
/**
* Debug variable resolution in a Meld file
*/
export async function debugResolutionCommand(options: DebugResolutionOptions): Promise<void> {
const { filePath, variableName, outputFormat = 'text' } = options;
console.log('DEBUG: debug-resolution command started with options', JSON.stringify(options));
console.log(chalk.blue('Debug resolution command started'));
console.log(chalk.blue('Options:'), JSON.stringify(options, null, 2));
try {
// Create service instances directly instead of using DI
console.log(chalk.blue('Creating services...'));
// Create the path operations service (needed for FileSystemService)
const pathOps = new PathOperationsService();
// Create the node file system implementation
const nodeFs = new NodeFileSystem();
// Create the base services first
const stateService = new StateService();
const fileSystemService = new FileSystemService(pathOps, nodeFs);
const parserService = new ParserService();
const pathService = new PathService();
// Initialize the path service
pathService.initialize(fileSystemService, parserService);
// Initialize paths in the state service
const resolvedPath = path.resolve(filePath);
const projectPath = path.dirname(resolvedPath);
console.log(chalk.blue('Project path:'), projectPath);
stateService.setPathVar('PROJECTPATH', projectPath);
stateService.setPathVar('.', projectPath);
stateService.setCurrentFilePath(filePath);
// Create services that depend on the base services
const resolutionService = new ResolutionService(
stateService,
fileSystemService,
parserService,
pathService
);
// Create the validation service
const validationService = new ValidationService();
// Create the interpreter service first
const interpreterService = new InterpreterService();
// Create the circularity service
const circularityService = new CircularityService();
// Create the directive service
const directiveService = new DirectiveService();
directiveService.initialize(
validationService,
stateService,
pathService,
fileSystemService,
parserService,
interpreterService, // Provide the interpreter service
circularityService, // CircularityService
resolutionService
);
// Now initialize the interpreter service with the directive service
interpreterService.initialize(directiveService, stateService);
// Register default handlers
directiveService.registerDefaultHandlers();
// Enable resolution tracking if a variable name is provided
let tracker: VariableResolutionTracker | undefined;
console.log(chalk.blue('Enabling resolution tracking...'));
if (typeof resolutionService.enableResolutionTracking === 'function') {
console.log(chalk.blue('enableResolutionTracking method found'));
const config: ResolutionTrackingConfig = {
enabled: true
};
if (variableName) {
console.log(chalk.blue(`Watching variable: ${variableName}`));
config.watchVariables = [variableName];
}
resolutionService.enableResolutionTracking(config);
console.log(chalk.blue('Resolution tracking enabled'));
} else {
console.warn(chalk.yellow('Resolution tracking is not available - enableResolutionTracking method missing'));
}
console.log(chalk.blue(`Processing file: ${filePath}`));
// Check if file exists
if (!await fileSystemService.exists(filePath)) {
console.error(chalk.red(`File not found: ${filePath}`));
return;
}
// Read file content
console.log(chalk.blue('Reading file content...'));
const fileContent = await fileSystemService.readFile(filePath);
console.log(chalk.blue('File content length:'), fileContent.length);
// Create a root state
console.log(chalk.blue('Creating root state...'));
const rootState = stateService;
// Parse the file
console.log(chalk.blue('Parsing file...'));
const nodes = await parserService.parse(fileContent);
console.log(chalk.blue('Parsed nodes:'), nodes.length);
// Process the file
console.log(chalk.blue('Interpreting nodes...'));
await interpreterService.interpret(nodes, {
initialState: rootState,
filePath,
mergeState: true
});
console.log(chalk.blue('Interpretation complete'));
// Get resolution attempts
let attempts: ResolutionAttempt[] = [];
if (typeof resolutionService.getResolutionTracker === 'function') {
console.log(chalk.blue('Getting resolution attempts...'));
attempts = resolutionService.getResolutionTracker()?.getAttempts() as ResolutionAttempt[] || [];
console.log(chalk.blue('Resolution attempts:'), attempts.length);
}
// Output results
if (outputFormat === 'json') {
console.log(JSON.stringify(attempts, null, 2));
} else {
// Group attempts by variable
console.log(chalk.blue('\nResolution attempts by variable:'));
const attemptsByVariable: Record<string, ResolutionAttempt[]> = {};
for (const attempt of attempts) {
if (!attemptsByVariable[attempt.variableName]) {
attemptsByVariable[attempt.variableName] = [];
}
attemptsByVariable[attempt.variableName].push(attempt);
}
// Display attempts by variable
for (const [variable, variableAttempts] of Object.entries(attemptsByVariable)) {
console.log(chalk.green(`\nVariable: ${variable}`));
for (const attempt of variableAttempts) {
const status = attempt.success ? chalk.green('✓') : chalk.red('✗');
const value = attempt.success ? chalk.cyan(JSON.stringify(attempt.value)) : chalk.red('not found');
const context = chalk.yellow(attempt.context);
console.log(` ${status} Context: ${context}`);
console.log(` Value: ${value}`);
if (attempt.source) {
console.log(` Source: ${chalk.magenta(attempt.source)}`);
}
if (attempt.contextBoundary) {
const boundaryType = attempt.contextBoundary.type;
const source = attempt.contextBoundary.sourceId || 'unknown';
const target = attempt.contextBoundary.targetId || 'unknown';
console.log(` Boundary: ${chalk.blue(boundaryType)} from ${source} to ${target}`);
}
}
}
// Summary
console.log(chalk.blue('\nSummary:'));
console.log(`Total attempts: ${attempts.length}`);
console.log(`Successful attempts: ${attempts.filter(a => a.success).length}`);
console.log(`Failed attempts: ${attempts.filter(a => !a.success).length}`);
}
} catch (error) {
console.error(chalk.red('Error:'), error);
if (error instanceof MeldResolutionError) {
console.error(chalk.red(`Resolution error: ${error.message}`));
if (error.details) {
console.error(chalk.red(`Details: ${JSON.stringify(error.details, null, 2)}`));
}
} else {
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
if (error instanceof Error && error.stack) {
console.error(chalk.dim(error.stack));
}
}
console.error(chalk.yellow('If this is a module resolution error, make sure you have built the codebase with "npm run build" before running debug commands'));
}
}