import * as fs from 'fs'; import * as path from 'path'; import { logError } from '../../utils'; import { extractPriorityPreviewFiles } from './projectEvidence'; /** * "프로젝트 지식 (project knowledge overview)" 문서 빌드 + 디스크 쓰기. * * 흐름: * 1) buildProjectKnowledgeMarkdown — localPathContext 를 받아 표준화된 Markdown * overview 본문 생성 (pure). * 2) writeProjectKnowledgeRecord — 그 본문을 `docs/records//development/` * 아래에 일자/슬러그 파일명으로 저장. * 3) buildProjectKnowledgeFallbackAnswer — 모델이 "추가 정보 필요" 류 회피 답변을 * 낼 때 덮어씌울 채팅용 답변 (markdown 초안 포함). * * Why one module: 셋 다 동일 helper (`getProjectDisplayName`, `extractPriority...`, * Path 파싱) 를 공유하고, 동일 사용 흐름 (knowledge 작성 turn) 의 일부라서 묶어 * 두는 게 god-file 다이어트 + 응집도 모두에 유리. * * Note: writeProjectKnowledgeRecord 는 fs I/O 가 들어가지만 transactionManager * 가 필요 없는 *append-only 신규 파일 생성* 경로 (덮어쓸 가능성 거의 0). 그래서 * 별도 transactional 처리 없이 pure-ish 모듈에 함께 둠. */ /** `ConnectAI` 디렉토리는 사용자가 `Astra` 로 부르므로 표시명 alias. 그 외는 그대로. */ export function getProjectDisplayName(projectPath: string): string { const projectName = path.basename(projectPath); return /^connectai$/i.test(projectName) ? 'Astra' : projectName; } /** localPathContext 의 `Path:` 라인 + tree + priority files 를 표준 markdown 으로 직렬화. */ export function buildProjectKnowledgeMarkdown(localPathContext: string): string { const pathMatch = localPathContext.match(/Path:\s*(.+)/); const projectPath = pathMatch?.[1]?.trim() || 'Unknown project path'; const projectDisplayName = getProjectDisplayName(projectPath); const treeMatch = localPathContext.match(/Scanned tree:\n([\s\S]*?)(?:\nPriority file previews:|$)/); const treePreview = treeMatch?.[1]?.trim().split('\n').slice(0, 80).join('\n') || ''; const priorityFiles = extractPriorityPreviewFiles(localPathContext); return [ `# ${projectDisplayName} Project Knowledge Overview`, '', `Date: ${new Date().toISOString()}`, `Project: ${projectDisplayName}`, `Repository: \`${projectPath}\``, '', '## Purpose', `${projectDisplayName}는 VS Code 안에서 로컬 AI 에이전트, Second Brain, 프로젝트 기록, 에이전트 스킬을 연결하는 개발 보조 프로젝트다.`, '', '## Confirmed Structure', '- `src/agent.ts`: 에이전트 실행, 로컬 경로 프리플라이트, Second Brain Trace, 액션 실행 흐름의 중심.', '- `src/sidebarProvider.ts`: Webview UI, 브레인/모델/프로젝트 선택, 프롬프트 전달, 기록 UI를 담당.', '- `src/features/secondBrainTrace.ts`: Second Brain 검색 결과와 근거 정책을 구성.', '- `src/features/projectChronicle/`: 프로젝트 기록을 Markdown으로 관리하는 Chronicle 기능.', '- `src/core/`: 큐, 이벤트, 트랜잭션, 오류 처리 등 실행 안정성 계층.', '- `tests/`: Second Brain, 로컬 경로 프리플라이트, Chronicle, 보안/트랜잭션 회귀 테스트.', '', '## Evidence Files', ...(priorityFiles.length ? priorityFiles.map((file) => `- \`${file}\``) : ['- 확인된 우선 파일 없음']), '', '## Scanned Tree Excerpt', '```text', treePreview || '(no scanned tree captured)', '```', '', '## Current Knowledge Gap', '- 전체 아키텍처는 파일 구조와 일부 프리뷰 기준으로 파악 가능하지만, 세부 동작 지식은 `src/agent.ts`, `src/sidebarProvider.ts`, `secondBrainTrace.ts`, `projectChronicle` 순서로 심화 분석해 보강해야 한다.', '', '## Next Records', '- `agent.ts` 실행 흐름 상세 분석', '- Second Brain Trace 검색 및 근거 정책 분석', '- Project Chronicle 기록 생성 흐름 분석', ].join('\n'); } /** * 프로젝트 지식 markdown 을 `docs/records//development/__…md` * 에 저장. Access 안 됐거나 projectPath 추출 실패 시 null. 쓰기 실패는 log 만 * 남기고 null 반환 (호출자가 fallback path 로 떨어지게). */ export function writeProjectKnowledgeRecord( localPathContext: string, ): { filePath: string; relativePath: string } | null { const pathMatch = localPathContext.match(/Path:\s*(.+)/); const projectPath = pathMatch?.[1]?.trim(); if (!projectPath || !localPathContext.includes('Access: succeeded')) return null; try { const projectName = path.basename(projectPath); const projectDisplayName = getProjectDisplayName(projectPath); const today = new Date().toISOString().slice(0, 10); const slug = projectDisplayName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'project'; const relativePath = path.join('docs', 'records', projectName, 'development', `${today}_${slug}_project_knowledge_overview.md`); const filePath = path.join(projectPath, relativePath); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, buildProjectKnowledgeMarkdown(localPathContext), 'utf8'); return { filePath, relativePath }; } catch (error: any) { logError('Failed to write project knowledge record.', { error: error?.message || String(error) }); return null; } } /** * 모델이 "추가 정보 필요" 류 회피 답변을 낼 때 덮어씌울 채팅용 답변. 사전 * 작성된 한국어 템플릿 안에 priorityFiles / treePreview / record 경로 만 동적으로 * 채워 넣는다. */ export function buildProjectKnowledgeFallbackAnswer( localPathContext: string, record?: { filePath: string; relativePath: string } | null, ): string { const pathMatch = localPathContext.match(/Path:\s*(.+)/); const projectPath = pathMatch?.[1]?.trim() || '제공된 로컬 프로젝트 경로'; const projectDisplayName = getProjectDisplayName(projectPath); const treeMatch = localPathContext.match(/Scanned tree:\n([\s\S]*?)(?:\nPriority file previews:|$)/); const treePreview = treeMatch?.[1]?.trim().split('\n').slice(0, 18).join('\n') || ''; const priorityMatches = extractPriorityPreviewFiles(localPathContext).slice(0, 10); const priorityText = priorityMatches.length ? priorityMatches.map((file) => `- ${file}`).join('\n') : '- package.json, src, docs, config 계열 파일을 우선 확인'; return [ '## 간단 요약', '맞아요. 이 경우에는 추가 질문으로 멈출 필요 없이, 지금 확인된 로컬 프로젝트 구조를 기준으로 기본 프로젝트 지식을 바로 만들면 됩니다.', '', '## 기본 지식 생성 방향', `대상 프로젝트는 \`${projectPath}\`입니다. 우선 MVP 지식은 “프로젝트 개요 + 주요 모듈 + 확인된 근거 파일 + 다음에 깊게 볼 영역” 형태로 만드는 것이 가장 안전합니다.`, '', '## 확인된 근거', priorityText, '', treePreview ? `## 확인된 구조 일부\n\`\`\`text\n${treePreview}\n\`\`\`` : '', '', '## 바로 만들 지식 초안', '```markdown', `# ${projectDisplayName} Project Knowledge Overview`, '', '## Purpose', `${projectDisplayName}는 VS Code 안에서 로컬 AI 에이전트, Second Brain, 프로젝트 기록, 에이전트 스킬을 연결하는 개발 보조 프로젝트다.`, '', '## Confirmed Structure', '- `src/agent.ts`: 에이전트 실행, 로컬 경로 프리플라이트, Second Brain Trace, 액션 실행 흐름의 중심.', '- `src/sidebarProvider.ts`: Webview UI, 브레인/모델/프로젝트 선택, 프롬프트 전달, 기록 UI를 담당.', '- `src/features/secondBrainTrace.ts`: Second Brain 검색 결과와 근거 정책을 구성.', '- `src/features/projectChronicle/`: 프로젝트 기록을 Markdown으로 관리하는 Chronicle 기능.', '- `src/core/`: 큐, 이벤트, 트랜잭션, 오류 처리 등 실행 안정성 계층.', '- `tests/`: Second Brain, 로컬 경로 프리플라이트, Chronicle, 보안/트랜잭션 회귀 테스트.', '', '## Current Knowledge Gap', '- 전체 아키텍처는 파일 구조와 일부 프리뷰 기준으로 파악 가능하지만, 세부 동작 지식은 `src/agent.ts`, `src/sidebarProvider.ts`, `secondBrainTrace.ts`, `projectChronicle` 순서로 심화 분석해 보강해야 한다.', '', '## Recommended Next Record', `- \`docs/records/${path.basename(projectPath)}/development/YYYY-MM-DD_${projectDisplayName.toLowerCase()}_project_knowledge_overview.md\``, '```', '', '## 다음 액션', record ? `프로젝트 지식 1번 문서를 생성했습니다: \`${record.filePath}\`` : '기본값으로는 위 초안을 프로젝트 지식 1번 문서로 저장하고, 그 다음 `agent.ts` 실행 흐름 지식을 별도 문서로 쪼개는 것이 좋습니다.', ].filter(Boolean).join('\n'); }