import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { AgentExecutor } from '../src/agent'; function stateStore() { const store = new Map(); return { get: (key: string) => store.get(key), update: async (key: string, value: any) => { store.set(key, value); } }; } describe('local project path preflight', () => { let root: string; beforeEach(() => { root = fs.mkdtempSync(path.join(os.tmpdir(), 'g1-local-preflight-')); fs.mkdirSync(path.join(root, 'src', 'features', 'game', 'systems'), { recursive: true }); fs.writeFileSync(path.join(root, 'package.json'), '{"scripts":{"dev":"vite"}}', 'utf8'); fs.writeFileSync(path.join(root, 'src', 'features', 'game', 'systems', 'CombatSystem.ts'), 'export class CombatSystem {}', 'utf8'); }); afterEach(() => { fs.rmSync(root, { recursive: true, force: true }); }); it('previews deep source files instead of treating a project folder as empty', () => { const context: any = { globalStorageUri: { fsPath: path.join(root, '.storage') }, workspaceState: stateStore(), globalState: stateStore() }; const agent = new AgentExecutor(context) as any; const inspected = agent.inspectLocalProjectPath(root, root); expect(inspected).toContain('Access: succeeded'); expect(inspected).toContain('Priority file previews'); expect(inspected).toContain('package.json'); expect(inspected).toContain('src/features/game/systems/CombatSystem.ts'); expect(inspected).toContain('export class CombatSystem'); }); it('removes upload requests when local project access already succeeded', () => { const context: any = { globalStorageUri: { fsPath: path.join(root, '.storage') }, workspaceState: stateStore(), globalState: stateStore() }; const agent = new AgentExecutor(context) as any; const answer = '코드를 업로드해 주시면 검토하겠습니다.'; const fixed = agent.enforceLocalPathReviewAnswer(answer, 'Access: succeeded\nPriority file previews:\n### package.json'); expect(fixed).toContain('제공된 로컬 프로젝트 경로에는 접근할 수 있고'); expect(fixed).not.toContain('코드를 업로드해 주시면'); }); it('treats project knowledge creation requests with local paths as inspectable work', () => { const context: any = { globalStorageUri: { fsPath: path.join(root, '.storage') }, workspaceState: stateStore(), globalState: stateStore() }; const agent = new AgentExecutor(context) as any; const prompt = '그러면 지금 /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트에 대한 지식을 만들면 되는거 아니야?'; expect(agent.shouldPreflightLocalProjectPath(prompt)).toBe(true); }); it('removes file-structure requests when knowledge creation path access already succeeded', () => { const context: any = { globalStorageUri: { fsPath: path.join(root, '.storage') }, workspaceState: stateStore(), globalState: stateStore() }; const agent = new AgentExecutor(context) as any; const answer = '프로젝트에 대한 지식을 만들고 싶다면, 먼저 해당 프로젝트의 핵심 파일이나 구조를 저에게 제공해 주셔야 합니다. 실제 구현 근거 없이는 유용한 지식을 만들 수 없습니다.'; const fixed = agent.enforceLocalPathReviewAnswer(answer, 'Access: succeeded\nPriority file previews:\n### package.json'); expect(fixed).toContain('프로젝트 지식을 만들 수 있습니다'); expect(fixed).not.toContain('핵심 파일이나 구조를 저에게 제공'); expect(fixed).not.toContain('실제 구현 근거 없이는'); }); it('replaces blocking scope questions for local project knowledge creation', () => { const context: any = { globalStorageUri: { fsPath: path.join(root, '.storage') }, workspaceState: stateStore(), globalState: stateStore() }; const agent = new AgentExecutor(context) as any; const answer = '## 블로킹 질문\n1. 어떤 기능 영역을 가장 먼저 분석하고 싶으신가요? (Question reason: 범위 조정)'; const localPathContext = [ 'Path: /Volumes/Data/project/Antigravity/ConnectAI', 'Access: succeeded', 'Type: directory', 'Scanned tree:', 'src/', 'src/agent.ts', 'Priority file previews:', '### package.json', '{"name":"g1nation"}', '### src/agent.ts', 'export class AgentExecutor {}' ].join('\n'); expect(agent.isBlockingProjectKnowledgeAnswer(answer)).toBe(true); const fallback = agent.buildProjectKnowledgeFallbackAnswer(localPathContext); expect(fallback).toContain('추가 질문으로 멈출 필요 없이'); expect(fallback).toContain('ConnectAI Project Knowledge Overview'); expect(fallback).not.toContain('어떤 기능 영역을 가장 먼저'); }); });