diff --git a/.gitignore b/.gitignore
index bc78471..bbd09d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -482,3 +482,6 @@ $RECYCLE.BIN/
# Vim temporary swap files
*.swp
+
+# Generated schema files
+*.dbml
diff --git a/src/AL2DBML.Application/Interfaces/IAlParser.cs b/src/AL2DBML.Application/Interfaces/IAlParser.cs
index 0f3689b..9839253 100644
--- a/src/AL2DBML.Application/Interfaces/IAlParser.cs
+++ b/src/AL2DBML.Application/Interfaces/IAlParser.cs
@@ -12,4 +12,5 @@ public interface IAlParser
DBMLTable ParseTableExtension(string alTableExtensionFileContent);
DBMLColumn ParseField(string alFieldContent);
OutputSchema GetOutputSchema();
+ void ClearOutputSchema();
}
diff --git a/src/AL2DBML.CLI/AL2DBML.CLI.csproj b/src/AL2DBML.CLI/AL2DBML.CLI.csproj
index 66629b4..2962959 100644
--- a/src/AL2DBML.CLI/AL2DBML.CLI.csproj
+++ b/src/AL2DBML.CLI/AL2DBML.CLI.csproj
@@ -12,4 +12,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AL2DBML.CLI/Commands/GenerateCommand.cs b/src/AL2DBML.CLI/Commands/GenerateCommand.cs
new file mode 100644
index 0000000..7ed30fb
--- /dev/null
+++ b/src/AL2DBML.CLI/Commands/GenerateCommand.cs
@@ -0,0 +1,60 @@
+using System.ComponentModel;
+using AL2DBML.Application.Interfaces;
+using AL2DBML.CLI.Services;
+using AL2DBML.CLI.Strategies;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace AL2DBML.CLI.Commands;
+
+public class GenerateSettings : CommandSettings
+{
+ [CommandOption("-i|--input ")]
+ [Description("The path to the AL project folder, AL file, or vscode workspace file of AL projects to parse.")]
+ public string InputPath { get; init; } = ".";
+
+ [CommandOption("-o|--output ")]
+ [Description("The path to the output directory.")]
+ public string OutputPath { get; init; } = ".";
+
+ [CommandOption("-n|--name ")]
+ [Description("The name of the output file (without extension). 'schema' by default.")]
+ public string OutputName { get; init; } = "schema";
+}
+
+public class GenerateCommand : AsyncCommand
+{
+ private readonly IAlParser _alParser;
+ private readonly IDBMLWriter _dbmlWriter;
+ private readonly IParsingTracker _tracker;
+
+ public GenerateCommand(IAlParser alParser, IDBMLWriter dbmlWriter, IParsingTracker tracker)
+ {
+ _alParser = alParser;
+ _dbmlWriter = dbmlWriter;
+ _tracker = tracker;
+ }
+
+ protected override async Task ExecuteAsync(CommandContext context, GenerateSettings settings, CancellationToken cancellationToken)
+ {
+ var inputType = FileSystemService.GetInputType(settings.InputPath);
+ if (inputType == Enums.InputType.NotSupported)
+ {
+ AnsiConsole.MarkupLine("[red]Error:[/] Unsupported input type. Please provide a valid directory, AL file, or vscode workspace file.");
+ return -1;
+ }
+
+ var outputPath = Path.Combine(settings.OutputPath, $"{settings.OutputName}.dbml");
+ Directory.CreateDirectory(settings.OutputPath);
+
+ var factory = new InputStrategyFactory(inputType, _alParser, _tracker);
+ var outputSchema = factory.Strategy.Execute(settings.InputPath);
+
+ var dbmlContent = await _dbmlWriter.WriteDBMLAsync(outputSchema);
+ await File.WriteAllTextAsync(outputPath, dbmlContent, cancellationToken);
+
+ AnsiConsole.MarkupLine($"[green]Done:[/] {_tracker.FileCount} file(s) parsed in {_tracker.Elapsed.TotalSeconds:F2}s → {Markup.Escape(outputPath)}");
+
+ return 0;
+ }
+}
diff --git a/src/AL2DBML.CLI/Enums/InputType.cs b/src/AL2DBML.CLI/Enums/InputType.cs
new file mode 100644
index 0000000..7083567
--- /dev/null
+++ b/src/AL2DBML.CLI/Enums/InputType.cs
@@ -0,0 +1,9 @@
+namespace AL2DBML.CLI.Enums;
+
+public enum InputType
+{
+ Directory,
+ ALFile,
+ WorkspaceFile,
+ NotSupported
+}
diff --git a/src/AL2DBML.CLI/Program.cs b/src/AL2DBML.CLI/Program.cs
index 3751555..1bcdaf0 100644
--- a/src/AL2DBML.CLI/Program.cs
+++ b/src/AL2DBML.CLI/Program.cs
@@ -1,2 +1,41 @@
-// See https://aka.ms/new-console-template for more information
-Console.WriteLine("Hello, World!");
+using AL2DBML.CLI.Commands;
+using AL2DBML.CLI.Services;
+using AL2DBML.DI;
+using Microsoft.Extensions.DependencyInjection;
+using Spectre.Console.Cli;
+
+
+
+// Register services
+var services = new ServiceCollection();
+services
+ .AddAL2Dbml()
+ .AddScoped()
+ .AddScoped();
+
+var registrar = new TypeRegistrar(services);
+
+var app = new CommandApp(registrar);
+
+app.Configure(config =>
+{
+ config.AddCommand("generate");
+});
+
+return await app.RunAsync(args);
+
+public sealed class TypeRegistrar(IServiceCollection services) : ITypeRegistrar
+{
+ public ITypeResolver Build() => new TypeResolver(services.BuildServiceProvider().CreateScope().ServiceProvider);
+
+ public void Register(Type service, Type implementation) => services.AddScoped(service, implementation);
+
+ public void RegisterInstance(Type service, object implementation) => services.AddSingleton(service, implementation);
+
+ public void RegisterLazy(Type service, Func