???? ?? ?? ?? ??? ?? fallback ???? ?? ??

- CodeLanguageCatalog? UTF-8 ???? ????? ?? fallback ???? ??? ??? manifest/build/test/lint ?? ?? ??? ????
- WorkspaceContextGenerator? ?? ??? ????? ?? Language Workflow ??? ?????? ??? no-LSP ?????? ?? ??? ?? ??? ?? ???
- AgentLoopLlmRequestPreparationService? ??? ?? tool-call ??? pre-call reminder ?? ??? AgentLoopService?? ???
- CodeLanguageCatalogTests, WorkspaceContextGeneratorTests, AgentLoopLlmRequestPreparationServiceTests? ??? ?? fallback/????/LLM ?? ?? ??? ???
- ??: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_final_batch\\ -p:IntermediateOutputPath=obj\\verify_final_batch\\ (?? 0 / ?? 0)
- ??: dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "CodeLanguageCatalogTests|WorkspaceContextGeneratorTests|AgentLoopLlmRequestPreparationServiceTests|AgentLoopIterationPreparationServiceTests|AgentMessageInvariantHelperTests|AgentToolResultBudgetTests|ChatStorageServiceTests|HtmlSkillGoldenReportTests|PptxSkillGoldenDeckTests|DocxSkillGoldenDocumentTests|ExcelSkillGoldenWorkbookTests" -p:OutputPath=bin\\verify_final_batch_tests\\ -p:IntermediateOutputPath=obj\\verify_final_batch_tests\\ (?? 54)
This commit is contained in:
2026-04-15 10:51:44 +09:00
parent 91c4dc74c3
commit 48e8c57cf3
11 changed files with 456 additions and 235 deletions

View File

@@ -20,12 +20,7 @@ public sealed record CodeLanguageCapability(
/// <summary>
/// 코드 탭과 에이전트가 공통으로 참조하는 언어 지원 카탈로그.
/// - 파일 분류
/// - 인덱싱 대상 확장자
/// - 시스템 프롬프트 언어 가이드
/// - LSP 연동 가능 언어
/// - 설정 UI 설명 문자열
/// 를 한 곳에서 관리합니다.
/// 파일 분류, 시스템 프롬프트 가이드, build/test/lint 힌트, LSP 가능 여부를 한 곳에서 관리합니다.
/// </summary>
public static class CodeLanguageCatalog
{
@@ -100,187 +95,187 @@ public static class CodeLanguageCatalog
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"),
"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"),
"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"),
"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"),
"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"),
"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"),
"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."
],
LspLanguageId: "go"),
"go",
"Go",
[".go", ".mod", ".sum"],
[
"Preserve package boundaries, error-first flow, and gofmt-style formatting.",
"Check interfaces, exported identifiers, and concurrency-sensitive changes together."
],
LspLanguageId: "go"),
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."
],
LspLanguageId: "rust"),
"rust",
"Rust",
[".rs", ".toml"],
[
"Respect Cargo workspace structure, ownership/borrowing rules, and crate boundaries.",
"Prefer explicit enums/results and verify compiler diagnostics after edits."
],
LspLanguageId: "rust"),
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."
],
LspLanguageId: "php"),
"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."
],
LspLanguageId: "php"),
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."
],
LspLanguageId: "ruby"),
"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."
],
LspLanguageId: "ruby"),
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."
],
LspLanguageId: "kotlin"),
"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."
],
LspLanguageId: "kotlin"),
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."
],
LspLanguageId: "swift"),
"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."
],
LspLanguageId: "swift"),
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."
]),
"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."
]),
"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."
]),
"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."
]),
"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."
]),
"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."
]),
"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 =
@@ -312,6 +307,7 @@ public static class CodeLanguageCatalog
{
if (string.IsNullOrWhiteSpace(key))
return null;
return s_byKey.TryGetValue(key.Trim(), out var found) ? found : null;
}
@@ -319,6 +315,7 @@ public static class CodeLanguageCatalog
{
if (string.IsNullOrWhiteSpace(extension))
return null;
return s_byExtension.TryGetValue(extension.Trim(), out var found) ? found : null;
}
@@ -380,6 +377,7 @@ public static class CodeLanguageCatalog
sb.Append(BuildLspSupportDescription());
return sb.ToString();
}
public static IReadOnlyList<string> GetManifestHints(string? key)
{
var normalizedKey = FindByKey(key)?.Key;
@@ -446,21 +444,45 @@ public static class CodeLanguageCatalog
return $"{capability.DisplayName}: {string.Join(" | ", parts)}";
}
public static IReadOnlyList<string> BuildWorkspaceWorkflowSummaries(
IEnumerable<string?> extensionsOrKeys,
string? preferredKey = null,
int maxLanguages = 3,
int maxHintsPerKind = 2)
{
var results = new List<string>();
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
void TryAppend(CodeLanguageCapability? capability)
{
if (capability == null || !seen.Add(capability.Key))
return;
var summary = BuildWorkflowSummary(capability.Key, maxHintsPerKind);
if (!string.IsNullOrWhiteSpace(summary))
results.Add(summary);
}
TryAppend(ResolveCapabilityFromKeyOrExtension(preferredKey));
foreach (var value in extensionsOrKeys ?? [])
{
TryAppend(ResolveCapabilityFromKeyOrExtension(value));
if (results.Count >= Math.Max(1, maxLanguages))
break;
}
return results;
}
public static string BuildFallbackSupportDescription()
=> "LSP 서버가 없거나 연결되지 않아도 확장자, 매니페스트, build/test/lint 힌트 기반의 정적 분석을 계속 제공합니다.";
=> "LSP 서버가 없거나 연결되지 않아도 확장자, 매니페스트, build/test/lint 힌트 기반의 정적 fallback 분석을 계속 제공합니다.";
public static string BuildFallbackSummary(string? filePathOrExtension)
{
CodeLanguageCapability? capability = null;
if (!string.IsNullOrWhiteSpace(filePathOrExtension))
{
capability = filePathOrExtension.StartsWith('.')
? FindByExtension(filePathOrExtension)
: FindByExtension(Path.GetExtension(filePathOrExtension));
}
var capability = ResolveCapabilityFromKeyOrExtension(filePathOrExtension);
if (capability == null)
return "정적 fallback: 확장자와 프로젝트 매니페스트를 먼저 확인하고 관련 build/test 명령 힌트를 따라 수동 검증하세요.";
return "정적 fallback: 확장자와 프로젝트 매니페스트를 먼저 확인하고 관련 build/test/lint 힌트를 따라 수동 검증하세요.";
var sb = new StringBuilder();
sb.AppendLine($"정적 fallback 분석: {capability.DisplayName}");
@@ -484,6 +506,30 @@ public static class CodeLanguageCatalog
return sb.ToString().TrimEnd();
}
private static CodeLanguageCapability? ResolveCapabilityFromKeyOrExtension(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
var normalized = value.Trim();
var capability = FindByKey(normalized);
if (capability != null)
return capability;
if (normalized.StartsWith('.'))
return FindByExtension(normalized);
capability = QuickSelectLanguages
.FirstOrDefault(x => string.Equals(x.QuickSelectKey, normalized, StringComparison.OrdinalIgnoreCase));
if (capability != null)
return capability;
var extension = Path.GetExtension(normalized);
return string.IsNullOrWhiteSpace(extension)
? null
: FindByExtension(extension);
}
private static void AppendHintBlock(List<string> parts, string label, IReadOnlyList<string> hints, int maxHintsPerKind)
{
if (hints.Count == 0)