에이전트 루프와 코드 언어 지원, PPT 생성 품질을 함께 고도화

- AgentCommandQueue를 도입해 실행 중 추가 입력을 우선순위와 인터럽트 여부까지 포함해 처리하도록 정리함
- AgentToolResultBudget와 AgentQueryContextBuilder에 tool result preview 캐시를 연결해 긴 세션에서 축약 결과 재사용을 안정화함
- CodeLanguageCatalog를 추가해 코드 탭의 내장 언어 지원, 인덱싱 확장자, 시스템 프롬프트 언어 가이드, LSP 언어 판정을 한 카탈로그로 통합함
- 설정의 코드 탭에 지원 언어(LSP)와 코드 탭 기본 지원 언어를 명시적으로 표시하도록 보강함
- DocumentPlannerTool의 presentation 구조를 컨설팅형 스토리라인으로 정리하고, PptxSkill에 executive_summary/recommendation/roadmap/comparison/kpi_dashboard 레이아웃을 추가함
- pptx-creator 스킬을 AX native pptx_create 중심으로 재작성하고, 관련 회귀 테스트를 추가했으며 WorkspaceContextGeneratorTests의 nullable 경고도 정리함

검증 결과
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_impl\\ -p:IntermediateOutputPath=obj\\verify_impl\\
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "CodeLanguageCatalogTests|AgentCommandQueueTests|AgentToolResultBudgetTests|DocumentPlannerPresentationTests|PptxSkillConsultingDeckTests" -p:OutputPath=bin\\verify_impl_tests\\ -p:IntermediateOutputPath=obj\\verify_impl_tests\\
This commit is contained in:
2026-04-14 19:53:39 +09:00
parent 946c31e275
commit 0b6d60e959
23 changed files with 1837 additions and 746 deletions

View File

@@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
namespace AxCopilot.Services;
public sealed record CodeLanguageCapability(
string Key,
string DisplayName,
IReadOnlyList<string> Extensions,
IReadOnlyList<string> Guidance,
string? LspLanguageId = null,
bool ShowInQuickSelect = false,
string? QuickSelectKey = null,
string? QuickSelectLabel = null,
string? QuickSelectIcon = null);
/// <summary>
/// 코드 탭과 에이전트가 공통으로 참조하는 언어 지원 카탈로그.
/// - 파일 분류
/// - 인덱싱 대상 확장자
/// - 시스템 프롬프트 언어 가이드
/// - LSP 연동 가능 언어
/// - 설정 UI 설명 문자열
/// 를 한 곳에서 관리합니다.
/// </summary>
public static class CodeLanguageCatalog
{
private static readonly ReadOnlyCollection<CodeLanguageCapability> s_all =
new(new List<CodeLanguageCapability>
{
new(
"csharp",
"C# (.NET)",
[".cs", ".csx", ".csproj", ".sln"],
[
"Use dotnet CLI, solution/project files, and NuGet package conventions.",
"Follow Microsoft naming conventions and prefer targeted edits over broad rewrites.",
"Verify impact on callers, DI registration, nullable flow, and build configuration."
],
LspLanguageId: "csharp",
ShowInQuickSelect: true,
QuickSelectKey: "csharp",
QuickSelectLabel: "C# (.NET)",
QuickSelectIcon: "\uD83D\uDD39"),
new(
"python",
"Python",
[".py", ".pyi", ".ipynb"],
[
"Use pip/venv or conda only if already available in the environment.",
"Follow PEP 8, type hints, and module/package boundaries.",
"Prefer small focused functions and verify import/runtime errors after edits."
],
LspLanguageId: "python",
ShowInQuickSelect: true,
QuickSelectKey: "python",
QuickSelectLabel: "Python",
QuickSelectIcon: "\uD83D\uDC0D"),
new(
"java",
"Java",
[".java", ".gradle", ".pom"],
[
"Use Maven or Gradle conventions already present in the repository.",
"Follow package structure, visibility rules, and style consistent with the existing codebase.",
"Check interfaces, implementations, and test fixtures together when modifying shared behavior."
],
LspLanguageId: "java",
ShowInQuickSelect: true,
QuickSelectKey: "java",
QuickSelectLabel: "Java",
QuickSelectIcon: "\u2615"),
new(
"cpp",
"C / C++",
[".c", ".cc", ".cxx", ".cpp", ".h", ".hh", ".hpp", ".inl"],
[
"Respect the repository's existing build system, usually CMake, MSBuild, or compiler-specific scripts.",
"Be careful with headers, include order, ownership, ABI-sensitive changes, and platform guards.",
"Validate both declaration and implementation impact when editing shared types."
],
LspLanguageId: "cpp",
ShowInQuickSelect: true,
QuickSelectKey: "cpp",
QuickSelectLabel: "C/C++",
QuickSelectIcon: "\u2699"),
new(
"typescript",
"TypeScript",
[".ts", ".tsx", ".mts", ".cts"],
[
"Use the existing package manager and tsconfig structure.",
"Prefer explicit types on public boundaries and check build/lint config before changing module format.",
"Preserve framework conventions already used by the project."
],
LspLanguageId: "typescript"),
new(
"javascript",
"JavaScript / Vue",
[".js", ".jsx", ".mjs", ".cjs", ".vue"],
[
"Use the existing Node package manager and lint/format rules.",
"For Vue, preserve the current component style and API pattern used by the project.",
"Check module boundaries, imports, and runtime side effects after edits."
],
LspLanguageId: "javascript",
ShowInQuickSelect: true,
QuickSelectKey: "javascript",
QuickSelectLabel: "JavaScript / Vue",
QuickSelectIcon: "\uD83C\uDF10"),
new(
"go",
"Go",
[".go", ".mod", ".sum"],
[
"Preserve package boundaries, error-first flow, and gofmt-style formatting.",
"Check interfaces, exported identifiers, and concurrency-sensitive changes together."
]),
new(
"rust",
"Rust",
[".rs", ".toml"],
[
"Respect Cargo workspace structure, ownership/borrowing rules, and crate boundaries.",
"Prefer explicit enums/results and verify compiler diagnostics after edits."
]),
new(
"php",
"PHP",
[".php", ".phtml"],
[
"Follow the framework and autoloading structure already present in the project.",
"Be careful with runtime includes, container wiring, and mixed template/application files."
]),
new(
"ruby",
"Ruby",
[".rb", ".rake", ".gemspec"],
[
"Preserve gem structure, Rails or plain Ruby conventions already used in the repository.",
"Check dynamic dispatch, concerns/modules, and tests together after edits."
]),
new(
"kotlin",
"Kotlin",
[".kt", ".kts"],
[
"Preserve Gradle structure, package layout, and nullability intent.",
"Be careful with JVM interop boundaries and Android-specific module structure when present."
]),
new(
"swift",
"Swift",
[".swift"],
[
"Preserve target structure, Apple framework imports, and protocol-oriented design already in use.",
"Check app lifecycle and platform-specific behavior after edits."
]),
new(
"scala",
"Scala",
[".scala", ".sc"],
[
"Respect sbt/module structure, functional style, and existing typeclass or Akka patterns if present.",
"Keep public APIs simple and avoid unnecessary type-level churn."
]),
new(
"shell",
"Shell",
[".sh", ".bash", ".zsh"],
[
"Prefer safe quoting, explicit exit handling, and repository-local scripts over one-off inline shell.",
"Check portability assumptions and environment-specific commands."
]),
new(
"powershell",
"PowerShell",
[".ps1", ".psm1", ".psd1", ".bat", ".cmd"],
[
"Prefer native PowerShell cmdlets and safe path handling.",
"Be careful with Windows-specific side effects, quoting, and admin-sensitive operations."
]),
new(
"sql",
"SQL",
[".sql"],
[
"Preserve migration ordering, transactional safety, and index/constraint compatibility.",
"Call out destructive or data-migrating changes explicitly."
]),
new(
"web",
"HTML / CSS / SCSS",
[".html", ".htm", ".css", ".scss", ".sass", ".less", ".xaml"],
[
"Preserve the existing design system, layout structure, and accessibility semantics.",
"Prefer incremental visual changes and keep selectors/components scoped."
]),
new(
"markup",
"JSON / YAML / XML / Markdown",
[".json", ".jsonc", ".xml", ".yaml", ".yml", ".md", ".txt"],
[
"Preserve schema shape, indentation style, and comment/document conventions already used in the repository.",
"Validate references, keys, and generated consumer impact after edits."
]),
});
private static readonly ReadOnlyDictionary<string, CodeLanguageCapability> s_byKey =
new(s_all.ToDictionary(x => x.Key, StringComparer.OrdinalIgnoreCase));
private static readonly ReadOnlyDictionary<string, CodeLanguageCapability> s_byExtension =
new(s_all
.SelectMany(cap => cap.Extensions.Select(ext => new KeyValuePair<string, CodeLanguageCapability>(ext, cap)))
.ToDictionary(x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase));
private static readonly HashSet<string> s_codeExtensions = new(
s_all.SelectMany(cap => cap.Extensions),
StringComparer.OrdinalIgnoreCase);
public static IReadOnlyList<CodeLanguageCapability> All => s_all;
public static IReadOnlyCollection<string> CodeExtensions => s_codeExtensions;
public static IReadOnlyList<CodeLanguageCapability> QuickSelectLanguages =>
s_all.Where(x => x.ShowInQuickSelect).ToList();
public static IReadOnlyList<CodeLanguageCapability> LspBackedLanguages =>
s_all.Where(x => !string.IsNullOrWhiteSpace(x.LspLanguageId)).ToList();
public static bool IsCodeLikeFile(string? extension)
=> !string.IsNullOrWhiteSpace(extension) && s_codeExtensions.Contains(extension);
public static CodeLanguageCapability? FindByKey(string? key)
{
if (string.IsNullOrWhiteSpace(key))
return null;
return s_byKey.TryGetValue(key.Trim(), out var found) ? found : null;
}
public static CodeLanguageCapability? FindByExtension(string? extension)
{
if (string.IsNullOrWhiteSpace(extension))
return null;
return s_byExtension.TryGetValue(extension.Trim(), out var found) ? found : null;
}
public static string? DetectLspLanguageId(string? filePath)
=> FindByExtension(Path.GetExtension(filePath ?? string.Empty))?.LspLanguageId;
public static string GetQuickSelectLabel(string? key)
=> FindByKey(key)?.DisplayName ?? key ?? "Auto";
public static string BuildSelectedLanguagePrompt(string? key)
{
if (string.IsNullOrWhiteSpace(key) || string.Equals(key, "auto", StringComparison.OrdinalIgnoreCase))
return string.Empty;
var capability = FindByKey(key);
if (capability == null)
return string.Empty;
return $"IMPORTANT: User selected language: {capability.DisplayName}. Prioritize this language for code analysis and generation.";
}
public static IEnumerable<string> GetGuidanceLines(string? selectedKey)
{
var selected = FindByKey(selectedKey);
if (selected != null)
{
foreach (var line in selected.Guidance)
yield return $"- {selected.DisplayName}: {line}";
yield break;
}
foreach (var capability in s_all.Where(x =>
x.Key is "csharp" or "python" or "java" or "cpp" or "typescript" or "javascript" or "go" or "rust" or "kotlin" or "swift"))
{
var summary = capability.Guidance.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(summary))
yield return $"- {capability.DisplayName}: {summary}";
}
}
public static string BuildLspSupportDescription()
=> string.Join(", ", LspBackedLanguages.Select(x => x.DisplayName));
public static string BuildStaticSupportDescription()
=> string.Join(", ", s_all.Select(x => x.DisplayName));
public static string BuildCodeTabSupportDescription()
{
var sb = new StringBuilder();
sb.Append("정적 분류/검색/프롬프트 지원: ");
sb.Append(BuildStaticSupportDescription());
sb.Append(" | LSP 심화 분석: ");
sb.Append(BuildLspSupportDescription());
return sb.ToString();
}
}