???? ?? ?? ?? ??? ?? 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:
14
README.md
14
README.md
@@ -1976,3 +1976,17 @@ MIT License
|
||||
- golden 회귀를 확대했습니다. [HtmlSkillGoldenReportTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/HtmlSkillGoldenReportTests.cs)에 strategy brief 시나리오를 추가했고, [PptxSkillGoldenDeckTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/PptxSkillGoldenDeckTests.cs)는 PMO steering deck까지 고정했습니다. [ArtifactQualityReviewServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/ArtifactQualityReviewServiceTests.cs), [ArtifactRepairGuideServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/ArtifactRepairGuideServiceTests.cs), [DeckQualityReviewServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/DeckQualityReviewServiceTests.cs), [DeckRepairGuideServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/DeckRepairGuideServiceTests.cs)도 함께 확장했습니다.
|
||||
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_doc_finish_batch\\ -p:IntermediateOutputPath=obj\\verify_doc_finish_batch\\` 경고 0 / 오류 0
|
||||
- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ArtifactQualityReviewServiceTests|ArtifactRepairGuideServiceTests|DeckQualityReviewServiceTests|DeckRepairGuideServiceTests|HtmlSkillGoldenReportTests|PptxSkillGoldenDeckTests|DocxSkillGoldenDocumentTests|ExcelSkillGoldenWorkbookTests" -p:OutputPath=bin\\verify_doc_finish_batch_tests\\ -p:IntermediateOutputPath=obj\\verify_doc_finish_batch_tests\\` 통과 26
|
||||
|
||||
업데이트: 2026-04-15 10:50 (KST)
|
||||
- 개발언어 fallback을 실제 코드 시스템 프롬프트와 워크스페이스 컨텍스트에 연결했습니다.
|
||||
- [CodeLanguageCatalog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/CodeLanguageCatalog.cs)를 UTF-8 기준으로 재정리하고, 언어별 `manifest/build/test/lint` 힌트를 조합하는 `BuildWorkspaceWorkflowSummaries()`를 추가했습니다.
|
||||
- [WorkspaceContextGenerator.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/WorkspaceContextGenerator.cs)는 새 `DetectLanguageWorkflowHints()`를 통해 `.ax-context.md`의 `## Language Workflow` 섹션과 런타임 힌트 생성을 같은 카탈로그 기준으로 공유합니다.
|
||||
- [ChatWindow.SystemPromptBuilder.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.SystemPromptBuilder.cs)는 코드 탭 시스템 프롬프트에 `## Repository Language Workflow` 섹션을 주입해 no-LSP 저장소에서도 실제 실행 가능한 힌트를 먼저 보게 했습니다.
|
||||
- 에이전틱 루프의 LLM 요청 준비를 helper로 더 분리했습니다.
|
||||
- 새 [AgentLoopLlmRequestPreparationService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopLlmRequestPreparationService.cs)가 초기 tool-call 강제 여부와 pre-call reminder 주입을 담당하고, [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs)는 orchestration 중심 구조를 더 유지하도록 정리했습니다.
|
||||
- 테스트를 추가/보강했습니다.
|
||||
- 새 [AgentLoopLlmRequestPreparationServiceTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/AgentLoopLlmRequestPreparationServiceTests.cs)
|
||||
- [CodeLanguageCatalogTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/CodeLanguageCatalogTests.cs), [WorkspaceContextGeneratorTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/WorkspaceContextGeneratorTests.cs) 확장
|
||||
- 검증:
|
||||
- `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
|
||||
|
||||
@@ -1116,3 +1116,30 @@ UI ?붿옄???洹쒕え 由ы뙥?좊쭅 ???꾪뿕 ?묒뾽 ??湲곕줉???덉쟾
|
||||
- 검증:
|
||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_doc_finish_batch\\ -p:IntermediateOutputPath=obj\\verify_doc_finish_batch\\`
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ArtifactQualityReviewServiceTests|ArtifactRepairGuideServiceTests|DeckQualityReviewServiceTests|DeckRepairGuideServiceTests|HtmlSkillGoldenReportTests|PptxSkillGoldenDeckTests|DocxSkillGoldenDocumentTests|ExcelSkillGoldenWorkbookTests" -p:OutputPath=bin\\verify_doc_finish_batch_tests\\ -p:IntermediateOutputPath=obj\\verify_doc_finish_batch_tests\\`
|
||||
|
||||
업데이트: 2026-04-15 10:50 (KST)
|
||||
- 개발언어 fallback 심화:
|
||||
- `CodeLanguageCatalog.cs`
|
||||
- 파일을 UTF-8 기준으로 재정리하고 중복 `BuildFallbackSupportDescription()`을 제거
|
||||
- `BuildWorkspaceWorkflowSummaries()` 추가
|
||||
- 키, quick select key, 확장자, 파일 경로를 모두 받아 capability로 정규화하는 `ResolveCapabilityFromKeyOrExtension()` 추가
|
||||
- `WorkspaceContextGenerator.cs`
|
||||
- `DetectLanguageWorkflowHints()` 추가
|
||||
- `.ax-context.md`의 `Language Workflow` 생성이 카탈로그 공용 API를 사용하도록 정리
|
||||
- `ChatWindow.SystemPromptBuilder.cs`
|
||||
- 코드 시스템 프롬프트에 `## Repository Language Workflow` 섹션 주입
|
||||
- no-LSP 저장소에서도 실제 manifest/build/test/lint 힌트를 prompt 안에서 직접 활용
|
||||
- 에이전틱 루프 분리:
|
||||
- 새 `AgentLoopLlmRequestPreparationService.cs`
|
||||
- 초기 tool-call 강제 여부 계산
|
||||
- pre-call tool reminder 삽입 여부 계산
|
||||
- 실제 LLM 전송용 `sendMessages` 배열 조립
|
||||
- `AgentLoopService.cs`
|
||||
- LLM 요청 전 메시지 조립 책임을 helper 호출로 대체해 orchestration 집중도 향상
|
||||
- 테스트 보강:
|
||||
- 새 `AgentLoopLlmRequestPreparationServiceTests.cs`
|
||||
- `CodeLanguageCatalogTests.cs`: fallback summary, workflow summary, workspace workflow dedupe/우선순위 검증
|
||||
- `WorkspaceContextGeneratorTests.cs`: preferred language 우선 `Language Workflow` 힌트 검증
|
||||
- 검증:
|
||||
- `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
|
||||
|
||||
@@ -158,6 +158,13 @@
|
||||
2. `tool_result replacement state`는 synthetic preview를 넘어 fingerprint 재바인딩까지 들어갔습니다. 남은 방향은 compact/branch 이후의 replacement policy를 세션 단위 상태로 더 오래 유지하는 것입니다.
|
||||
3. 문서 포맷은 `ArtifactQualityOutputFormatter`가 HTML/XLSX뿐 아니라 DOCX/PPTX까지 확장되었습니다. 다음 마감은 포맷별 critic/repair 자체를 더 깊게 하고, golden fixture 샘플을 확대하는 단계입니다.
|
||||
|
||||
업데이트: 2026-04-15 10:50 (KST)
|
||||
|
||||
### 추가 진행 메모
|
||||
1. 개발언어 fallback은 이제 `Language Workflow`를 `.ax-context.md`와 코드 시스템 프롬프트에 모두 주입하는 단계까지 올라왔습니다. 남은 마감은 no-LSP 환경에서 이 힌트를 실제 도구 사용/검증 흐름과 더 촘촘히 묶는 것입니다.
|
||||
2. 에이전틱 루프는 `run lifecycle`, `queued command projection`, `iteration preparation`, `LLM request preparation` helper까지 분리됐습니다. 다음 큰 축은 `RunAsync`의 tool dispatch/finalize 분리를 더 진행해 본체 책임을 더 줄이는 것입니다.
|
||||
3. 명령/스킬 합성은 우선순위 충돌 해소가 대부분 정리됐고, 이후 작업은 릴리즈 게이트와 체크리스트를 최종 상태에 맞춰 닫는 단계입니다.
|
||||
|
||||
업데이트: 2026-04-15 10:10 (KST)
|
||||
|
||||
### 통합 마감 계획
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using AxCopilot.Models;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class AgentLoopLlmRequestPreparationServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Prepare_ShouldInjectToolReminderOnFirstForcedCall()
|
||||
{
|
||||
var queryMessages = new List<ChatMessage>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Role = "user",
|
||||
Content = "inspect the repository"
|
||||
}
|
||||
};
|
||||
|
||||
var result = AgentLoopLlmRequestPreparationService.Prepare(
|
||||
queryMessages,
|
||||
totalToolCalls: 0,
|
||||
forceInitialToolCallEnabled: true,
|
||||
injectPreCallToolReminder: true,
|
||||
noToolCallLoopRetry: 0);
|
||||
|
||||
result.ForceInitialToolCall.Should().BeTrue();
|
||||
result.InjectedToolReminder.Should().BeTrue();
|
||||
result.SendMessages.Should().HaveCount(2);
|
||||
result.SendMessages.Last().Content.Should().Contain("[TOOL_REQUIRED]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Prepare_ShouldSkipReminderWhenRetryLoopIsAlreadyActive()
|
||||
{
|
||||
var queryMessages = new List<ChatMessage>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Role = "user",
|
||||
Content = "inspect the repository"
|
||||
}
|
||||
};
|
||||
|
||||
var result = AgentLoopLlmRequestPreparationService.Prepare(
|
||||
queryMessages,
|
||||
totalToolCalls: 0,
|
||||
forceInitialToolCallEnabled: true,
|
||||
injectPreCallToolReminder: true,
|
||||
noToolCallLoopRetry: 1);
|
||||
|
||||
result.ForceInitialToolCall.Should().BeTrue();
|
||||
result.InjectedToolReminder.Should().BeFalse();
|
||||
result.SendMessages.Should().HaveCount(1);
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ public class CodeLanguageCatalogTests
|
||||
summary.Should().Contain("go build ./...");
|
||||
summary.Should().Contain("go test ./...");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWorkflowSummary_ShouldExposeActionableWorkflowHints()
|
||||
{
|
||||
@@ -77,4 +78,18 @@ public class CodeLanguageCatalogTests
|
||||
CodeLanguageCatalog.GetTestHints("kotlin").Should().Contain("./gradlew test");
|
||||
CodeLanguageCatalog.GetLintHints("javascript").Should().Contain("eslint .");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWorkspaceWorkflowSummaries_ShouldPreferSelectedLanguageAndDedupeEntries()
|
||||
{
|
||||
var summaries = CodeLanguageCatalog.BuildWorkspaceWorkflowSummaries(
|
||||
[".go", ".py", ".go", ".rs"],
|
||||
preferredKey: "python",
|
||||
maxLanguages: 3);
|
||||
|
||||
summaries.Should().HaveCount(3);
|
||||
summaries[0].Should().StartWith("Python:");
|
||||
summaries.Should().Contain(summary => summary.StartsWith("Go:"));
|
||||
summaries.Should().Contain(summary => summary.StartsWith("Rust:"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,4 +338,27 @@ public class WorkspaceContextGeneratorTests
|
||||
Directory.Delete(tempDir, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectLanguageWorkflowHints_ShouldPreferSelectedLanguage()
|
||||
{
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempDir);
|
||||
try
|
||||
{
|
||||
File.WriteAllText(Path.Combine(tempDir, "main.go"), "package main");
|
||||
File.WriteAllText(Path.Combine(tempDir, "worker.go"), "package main");
|
||||
File.WriteAllText(Path.Combine(tempDir, "helper.py"), "print('hi')");
|
||||
|
||||
var hints = WorkspaceContextGenerator.DetectLanguageWorkflowHints(tempDir, preferredLanguage: "python");
|
||||
|
||||
hints.Should().NotBeEmpty();
|
||||
hints[0].Should().StartWith("Python:");
|
||||
hints.Should().Contain(hint => hint.StartsWith("Go:"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDir, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using AxCopilot.Models;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
internal sealed record AgentLoopLlmRequestPreparationResult(
|
||||
List<ChatMessage> SendMessages,
|
||||
bool ForceInitialToolCall,
|
||||
bool InjectedToolReminder);
|
||||
|
||||
/// <summary>
|
||||
/// query view가 만들어진 뒤 실제 LLM 요청 배열을 조립합니다.
|
||||
/// 초기 tool call 강제 여부와 사전 reminder 주입을 한곳에서 결정해
|
||||
/// AgentLoopService 본체가 orchestration에 더 집중하도록 분리합니다.
|
||||
/// </summary>
|
||||
internal static class AgentLoopLlmRequestPreparationService
|
||||
{
|
||||
public static AgentLoopLlmRequestPreparationResult Prepare(
|
||||
IReadOnlyList<ChatMessage> queryMessages,
|
||||
int totalToolCalls,
|
||||
bool forceInitialToolCallEnabled,
|
||||
bool injectPreCallToolReminder,
|
||||
int noToolCallLoopRetry)
|
||||
{
|
||||
var forceInitialToolCall = totalToolCalls == 0 && forceInitialToolCallEnabled;
|
||||
if (!forceInitialToolCall
|
||||
|| !injectPreCallToolReminder
|
||||
|| noToolCallLoopRetry > 0)
|
||||
{
|
||||
return new AgentLoopLlmRequestPreparationResult(
|
||||
queryMessages.ToList(),
|
||||
forceInitialToolCall,
|
||||
false);
|
||||
}
|
||||
|
||||
var sendMessages = queryMessages.ToList();
|
||||
sendMessages.Add(BuildToolReminderMessage());
|
||||
return new AgentLoopLlmRequestPreparationResult(
|
||||
sendMessages,
|
||||
forceInitialToolCall,
|
||||
true);
|
||||
}
|
||||
|
||||
internal static ChatMessage BuildToolReminderMessage()
|
||||
{
|
||||
return new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[TOOL_REQUIRED] 지금 즉시 <tool_call> 형식으로 도구를 호출하세요. 텍스트만 반환하면 거부됩니다.\n" +
|
||||
"Output format:\n<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -87,22 +87,6 @@ public partial class AgentLoopService
|
||||
requestInterrupt && IsRunning);
|
||||
}
|
||||
|
||||
private void DrainPendingCommands(List<ChatMessage> messages)
|
||||
{
|
||||
var queuedSnapshot = _pendingCommands.Snapshot();
|
||||
if (queuedSnapshot.Count == 0)
|
||||
return;
|
||||
|
||||
var drained = _pendingCommands.DequeuePriorityBatch();
|
||||
if (drained.Count == 0)
|
||||
return;
|
||||
|
||||
var projection = AgentQueuedCommandProjector.Project(drained, queuedSnapshot.Count - drained.Count);
|
||||
messages.AddRange(projection.Messages);
|
||||
foreach (var evt in projection.Events)
|
||||
EmitEvent(evt.Type, evt.ToolName, evt.Summary);
|
||||
}
|
||||
|
||||
/// <summary>에이전트 이벤트 스트림 (UI 바인딩용).</summary>
|
||||
public ObservableCollection<AgentEvent> Events { get; } = new();
|
||||
|
||||
@@ -557,25 +541,14 @@ public partial class AgentLoopService
|
||||
EmitEvent(AgentEventType.Error, "", "현재 스킬 런타임 정책으로 사용 가능한 도구가 없습니다.");
|
||||
return "⚠ 현재 스킬 정책에서 허용된 도구가 없어 작업을 진행할 수 없습니다. allowed-tools 설정을 확인하세요.";
|
||||
}
|
||||
// totalToolCalls == 0: 아직 한 번도 도구를 안 불렀으면 tool_choice:"required" 강제
|
||||
// → chatty 모델(Qwen 등)이 텍스트 설명만 하고 도구를 안 부르는 현상 방지
|
||||
var forceFirst = totalToolCalls == 0 && executionPolicy.ForceInitialToolCall;
|
||||
|
||||
// IBM/Qwen 등 chatty 모델 대응: 첫 번째 호출 직전 마지막 user 메시지로 도구 호출 강제 reminder 주입.
|
||||
// recovery 메시지가 이미 추가된 경우(NoToolCallLoopRetry > 0)에는 중복 주입하지 않음.
|
||||
// 임시 메시지이므로 실제 messages 목록은 수정하지 않고, 별도 sendMessages로 전달.
|
||||
List<ChatMessage> sendMessages = queryMessages;
|
||||
if (forceFirst
|
||||
&& executionPolicy.InjectPreCallToolReminder
|
||||
&& runState.NoToolCallLoopRetry == 0)
|
||||
{
|
||||
sendMessages = [.. queryMessages, new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[TOOL_REQUIRED] 지금 즉시 <tool_call> 형식으로 도구를 호출하세요. 텍스트만 반환하면 거부됩니다.\n" +
|
||||
"Output format:\n<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>"
|
||||
}];
|
||||
}
|
||||
var llmRequest = AgentLoopLlmRequestPreparationService.Prepare(
|
||||
queryMessages,
|
||||
totalToolCalls,
|
||||
executionPolicy.ForceInitialToolCall,
|
||||
executionPolicy.InjectPreCallToolReminder,
|
||||
runState.NoToolCallLoopRetry);
|
||||
var forceFirst = llmRequest.ForceInitialToolCall;
|
||||
var sendMessages = llmRequest.SendMessages;
|
||||
|
||||
// 워크플로우 상세 로그: LLM 요청
|
||||
llmCallSw.Restart();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -174,6 +175,21 @@ internal static class WorkspaceContextGenerator
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<string> DetectLanguageWorkflowHints(
|
||||
string? workFolder,
|
||||
string? preferredLanguage = null,
|
||||
int maxLanguages = 3)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(workFolder) || !Directory.Exists(workFolder))
|
||||
return [];
|
||||
|
||||
var extDist = GetExtensionDistribution(workFolder, CancellationToken.None);
|
||||
return CodeLanguageCatalog.BuildWorkspaceWorkflowSummaries(
|
||||
extDist.Select(x => x.Key),
|
||||
preferredLanguage,
|
||||
maxLanguages);
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// 분석 로직
|
||||
// ════════════════════════════════════════════════════════════
|
||||
@@ -450,26 +466,7 @@ internal static class WorkspaceContextGenerator
|
||||
.ToList();
|
||||
|
||||
private static List<string> BuildLanguageWorkflow(List<KeyValuePair<string, int>> extDist)
|
||||
{
|
||||
var workflow = new List<string>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var (extension, _) in extDist)
|
||||
{
|
||||
var capability = Services.CodeLanguageCatalog.FindByExtension(extension);
|
||||
if (capability == null || !seen.Add(capability.Key))
|
||||
continue;
|
||||
|
||||
var summary = Services.CodeLanguageCatalog.BuildWorkflowSummary(capability.Key);
|
||||
if (!string.IsNullOrWhiteSpace(summary))
|
||||
workflow.Add(summary);
|
||||
|
||||
if (workflow.Count >= 3)
|
||||
break;
|
||||
}
|
||||
|
||||
return workflow;
|
||||
}
|
||||
=> CodeLanguageCatalog.BuildWorkspaceWorkflowSummaries(extDist.Select(x => x.Key)).ToList();
|
||||
|
||||
private static async Task<(string? Branch, string? Remote)> GetGitInfoAsync(
|
||||
string folder, CancellationToken ct)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -290,6 +290,15 @@ public partial class ChatWindow
|
||||
foreach (var guidance in CodeLanguageCatalog.GetGuidanceLines(_selectedLanguage == "auto" ? null : _selectedLanguage))
|
||||
sb.AppendLine(guidance);
|
||||
sb.AppendLine("- Fallback: If LSP is unavailable, continue with extension-based detection, manifest files, and likely build/test/lint commands instead of stopping.");
|
||||
var repositoryLanguageWorkflow = WorkspaceContextGenerator.DetectLanguageWorkflowHints(
|
||||
workFolder,
|
||||
_selectedLanguage == "auto" ? null : _selectedLanguage);
|
||||
if (repositoryLanguageWorkflow.Count > 0)
|
||||
{
|
||||
sb.AppendLine("\n## Repository Language Workflow");
|
||||
foreach (var workflow in repositoryLanguageWorkflow)
|
||||
sb.AppendLine("- " + workflow);
|
||||
}
|
||||
|
||||
// 코드 품질 + 안전 수칙
|
||||
sb.AppendLine("\n## Code Quality & Safety");
|
||||
|
||||
Reference in New Issue
Block a user