diff --git a/src/Main/CLI/Commands/ConfigCommand.cs b/src/Main/CLI/Commands/ConfigCommand.cs index fb623fb61..76e024b13 100644 --- a/src/Main/CLI/Commands/ConfigCommand.cs +++ b/src/Main/CLI/Commands/ConfigCommand.cs @@ -81,48 +81,9 @@ public override async Task ExecuteAsync( } else { - // Default: show entire configuration with all nodes - var config = this.Config; - output = new - { - Nodes = config.Nodes.Select(kvp => new NodeDetailsDto - { - NodeId = kvp.Key, - Access = kvp.Value.Access.ToString(), - ContentIndex = new ContentIndexConfigDto - { - Type = kvp.Value.ContentIndex.Type.ToString(), - Path = kvp.Value.ContentIndex is KernelMemory.Core.Config.ContentIndex.SqliteContentIndexConfig sqlite - ? sqlite.Path - : null - }, - FileStorage = kvp.Value.FileStorage != null ? new StorageConfigDto - { - Type = kvp.Value.FileStorage.Type.ToString() - } : null, - RepoStorage = kvp.Value.RepoStorage != null ? new StorageConfigDto - { - Type = kvp.Value.RepoStorage.Type.ToString() - } : null, - SearchIndexes = kvp.Value.SearchIndexes.Select(si => new SearchIndexDto - { - Type = si.Type.ToString() - }).ToList() - }).ToList(), - Cache = new CacheInfoDto - { - EmbeddingsCache = config.EmbeddingsCache != null ? new CacheConfigDto - { - Type = config.EmbeddingsCache.Type.ToString(), - Path = config.EmbeddingsCache.Path - } : null, - LlmCache = config.LLMCache != null ? new CacheConfigDto - { - Type = config.LLMCache.Type.ToString(), - Path = config.LLMCache.Path - } : null - } - }; + // Default: show the actual AppConfig structure (not DTOs) + // This allows users to copy/paste the output into their config file + output = this.Config; } formatter.Format(output); diff --git a/tests/Main.Tests/Integration/ConfigCommandTests.cs b/tests/Main.Tests/Integration/ConfigCommandTests.cs index 50eead491..98706933d 100644 --- a/tests/Main.Tests/Integration/ConfigCommandTests.cs +++ b/tests/Main.Tests/Integration/ConfigCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using KernelMemory.Core.Config; +using KernelMemory.Core.Config.Cache; using KernelMemory.Main.CLI.Commands; using Spectre.Console.Cli; using Xunit; @@ -32,7 +33,9 @@ public ConfigCommandTests() ["personal"] = NodeConfig.CreateDefaultPersonalNode(Path.Combine(this._tempDir, "nodes", "personal")), ["work"] = NodeConfig.CreateDefaultPersonalNode(Path.Combine(this._tempDir, "nodes", "work")), ["shared"] = NodeConfig.CreateDefaultPersonalNode(Path.Combine(this._tempDir, "nodes", "shared")) - } + }, + EmbeddingsCache = CacheConfig.CreateDefaultSqliteCache(Path.Combine(this._tempDir, "embeddings-cache.db")), + LLMCache = null }; var json = System.Text.Json.JsonSerializer.Serialize(config); @@ -109,6 +112,65 @@ public void ConfigCommand_WithoutFlags_ShouldShowEntireConfiguration() } } + [Fact] + public void ConfigCommand_OutputStructure_ShouldMatchAppConfigFormat() + { + // This test verifies the BUG: km config output should match AppConfig structure + // so users can copy/paste the output back into their config file + + // Arrange + var config = ConfigParser.LoadFromFile(this._configPath); + + var settings = new ConfigCommandSettings + { + ConfigPath = this._configPath, + Format = "json" + }; + + var command = new ConfigCommand(config); + var context = new CommandContext( + new[] { "--config", this._configPath }, + new EmptyRemainingArguments(), + "config", + null); + + // Capture stdout + using var outputCapture = new StringWriter(); + var originalOutput = Console.Out; + Console.SetOut(outputCapture); + + try + { + // Act + var exitCode = command.ExecuteAsync(context, settings).GetAwaiter().GetResult(); + + // Assert + Assert.Equal(Constants.ExitCodeSuccess, exitCode); + + var output = outputCapture.ToString(); + var outputJson = System.Text.Json.JsonDocument.Parse(output); + + // BUG: Current output has "nodes" as an array + // EXPECTED: "nodes" should be an object/dictionary (like in config file) + var nodesElement = outputJson.RootElement.GetProperty("nodes"); + Assert.Equal(System.Text.Json.JsonValueKind.Object, nodesElement.ValueKind); + // ^ This will FAIL with current code (it's an Array) + + // BUG: Current output has "embeddingsCache" nested under "cache" + // EXPECTED: "embeddingsCache" should be at root level (like in config file) + Assert.True(outputJson.RootElement.TryGetProperty("embeddingsCache", out _)); + // ^ This will FAIL with current code (no "embeddingsCache" at root) + + // Should NOT have a "cache" wrapper + Assert.False(outputJson.RootElement.TryGetProperty("cache", out _)); + // ^ This will FAIL with current code (it has "cache" wrapper) + } + finally + { + Console.SetOut(originalOutput); + } + } + [Fact] public void ConfigCommand_WithShowNodesFlag_ShouldShowAllNodesSummary() {