diff --git a/package.json b/package.json index 14cc72e..7d35c5a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "g1nation", "displayName": "G1nation", "description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.", - "version": "2.53.0", + "version": "2.54.0", "publisher": "connectailab", "license": "MIT", "icon": "assets/icon.png", diff --git a/src/agent.ts b/src/agent.ts index 172d45c..a7e5499 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -287,6 +287,12 @@ export class AgentExecutor { if (recentProjectKnowledgeContext) { contextBlock += `\n\n${recentProjectKnowledgeContext}`; } + const projectBriefContext = prompt && loopDepth === 0 + ? this.buildJarvisProjectBriefContext(prompt, localPathContext, recentProjectKnowledgeContext) + : ''; + if (projectBriefContext) { + contextBlock += `\n\n${projectBriefContext}`; + } // 2. Setup History if (prompt !== null) { @@ -339,12 +345,15 @@ export class AgentExecutor { const localProjectKnowledgeCtx = prompt && localPathContext && this.isProjectKnowledgeCreationRequest(prompt) ? `\n\n[LOCAL PROJECT KNOWLEDGE CREATION OVERRIDE]\nThe user gave an accessible local project path and asked to create project knowledge. Do not ask blocking scope questions. Use a sensible default MVP: create or propose a project overview note from the inspected tree and priority file previews. If writing is not explicitly safe, provide the concrete note draft and target path.` : ''; + const thinkingPartnerCtx = prompt && this.isThinkingPartnerRequest(prompt) + ? `\n\n[JARVIS THINKING PARTNER MODE]\nThe user is using this tool to clarify project direction, not just to receive generic advice. Give a clear opinionated verdict first. Then separate confirmed facts, inferences, concerns, decision forks, and the next small action. Do not merely say the direction is good. If evidence is thin, say exactly what is missing and what file or record should be checked next.` + : ''; const secondBrainTraceCtx = secondBrainTrace ? `\n\n${renderSecondBrainTraceContext(secondBrainTrace)}` : ''; const memoryCtx = this.buildMemoryContext(prompt || '', activeBrain); - const fullSystemPrompt = `${agentSkillCtx}\n\n${systemPrompt}${internetCtx}${memoryCtx}${designerCtx}${localProjectKnowledgeCtx}${secondBrainTraceCtx}\n\n[CONTEXT]\n${brainContext}${brainInventoryCtx}\n${contextBlock}${negativeCtx}`; + const fullSystemPrompt = `${agentSkillCtx}\n\n${systemPrompt}${internetCtx}${memoryCtx}${designerCtx}${localProjectKnowledgeCtx}${thinkingPartnerCtx}${secondBrainTraceCtx}\n\n[CONTEXT]\n${brainContext}${brainInventoryCtx}\n${contextBlock}${negativeCtx}`; const messagesForRequest: ChatMessage[] = [ { role: 'system', content: fullSystemPrompt, internal: true }, ...reqMessages @@ -766,7 +775,7 @@ export class AgentExecutor { } private shouldPreflightLocalProjectPath(prompt: string): boolean { - return /(검토|리뷰|분석|확인|봐줘|고쳐|개선|디버그|지식|문서화|문서|정리|기록|위키|저장|만들|생성|knowledge|document|documentation|wiki|summari[sz]e|review|analy[sz]e|inspect|debug|fix|improve)/i.test(prompt) + return /(검토|리뷰|분석|확인|봐줘|고쳐|개선|디버그|지식|문서화|문서|정리|기록|위키|저장|만들|생성|설계|아키텍처|구조|방향|의견|생각|판단|어떤\s*거?\s*같|어때|knowledge|document|documentation|wiki|summari[sz]e|review|analy[sz]e|inspect|debug|fix|improve|architecture|design|structure|opinion|think|judge)/i.test(prompt) && /\/Volumes\/Data\/project\/Antigravity\/[^\s`"'<>]+/i.test(prompt); } @@ -779,6 +788,62 @@ export class AgentExecutor { return /(아키텍처|구조|조사|분석|설계|흐름|모듈|역할|개선|architecture|structure|design|flow|module|investigate|analy[sz]e)/i.test(prompt); } + private isThinkingPartnerRequest(prompt: string): boolean { + return /(어떤\s*거?\s*같|어때|어떻게\s*생각|의견|판단|방향|설계|아키텍처|구조|자비스|생각.*정리|갈림길|architecture|design|direction|opinion|think|judge)/i.test(prompt); + } + + private buildJarvisProjectBriefContext(prompt: string, localPathContext: string, recentProjectKnowledgeContext: string): string { + if (!this.isThinkingPartnerRequest(prompt)) { + return ''; + } + + const sourceContext = localPathContext && localPathContext.includes('Access: succeeded') + ? localPathContext + : recentProjectKnowledgeContext; + if (!sourceContext) { + return [ + '[JARVIS PROJECT BRIEF]', + 'No concrete local project brief is available yet.', + 'Use the conversation and Second Brain cautiously. If the user asks about a project architecture, ask for or inspect the project path before making strong claims.', + '', + this.buildThinkingPartnerResponseContract() + ].join('\n'); + } + + const projectPath = sourceContext.match(/Path:\s*(.+)/)?.[1]?.trim() + || sourceContext.match(/Repository:\s*`([^`]+)`/)?.[1]?.trim() + || sourceContext.match(/project evidence:\s*([^\s]+)/i)?.[1]?.trim() + || 'current project'; + const evidenceFiles = sourceContext.includes('Priority file previews:') + ? this.extractPriorityPreviewFiles(sourceContext).slice(0, 10) + : this.extractEvidenceFilesFromProjectKnowledge(sourceContext).slice(0, 10); + const treeMatch = sourceContext.match(/Scanned tree:\n([\s\S]*?)(?:\nPriority file previews:|$)/); + const treePreview = treeMatch?.[1]?.trim().split('\n').slice(0, 30).join('\n') || ''; + + return [ + '[JARVIS PROJECT BRIEF]', + `Project evidence target: ${projectPath}`, + evidenceFiles.length + ? `Evidence files available:\n${evidenceFiles.map((file) => `- ${file}`).join('\n')}` + : 'Evidence files available: not enough concrete file markers were found.', + treePreview ? `Visible structure preview:\n${treePreview}` : '', + '', + this.buildThinkingPartnerResponseContract() + ].filter(Boolean).join('\n'); + } + + private buildThinkingPartnerResponseContract(): string { + return [ + 'Thinking partner response contract:', + '1. Start with a direct verdict, not a generic compliment.', + '2. Separate confirmed facts from inferences.', + '3. Name the strongest part of the direction and the weakest/missing part.', + '4. Identify the real decision fork the user is facing.', + '5. Suggest one small next action that would make the project direction clearer.', + '6. If project evidence is thin, say what must be inspected next instead of pretending certainty.' + ].join('\n'); + } + private buildRequestHistory(history: ChatMessage[]): ChatMessage[] { return history.map((message) => { if (message.role !== 'assistant' || typeof message.content !== 'string') { diff --git a/tests/localPathPreflight.test.ts b/tests/localPathPreflight.test.ts index 1e8380f..99fdc7d 100644 --- a/tests/localPathPreflight.test.ts +++ b/tests/localPathPreflight.test.ts @@ -70,6 +70,19 @@ describe('local project path preflight', () => { expect(agent.shouldPreflightLocalProjectPath(prompt)).toBe(true); }); + it('treats architecture opinion 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); + expect(agent.isThinkingPartnerRequest(prompt)).toBe(true); + }); + it('removes file-structure requests when knowledge creation path access already succeeded', () => { const context: any = { globalStorageUri: { fsPath: path.join(root, '.storage') }, @@ -299,4 +312,40 @@ describe('local project path preflight', () => { expect(fixed).toContain('`src/agent.ts`'); expect(fixed).toContain('로컬 프로젝트 경로'); }); + + it('builds a Jarvis project brief for architecture thinking-partner questions', () => { + const context: any = { + globalStorageUri: { fsPath: path.join(root, '.storage') }, + workspaceState: stateStore(), + globalState: stateStore() + }; + const agent = new AgentExecutor(context) as any; + const localPathContext = [ + 'Path: /Volumes/Data/project/Antigravity/ConnectAI', + 'Access: succeeded', + 'Type: directory', + 'Scanned tree:', + 'package.json', + 'src/agent.ts', + 'src/features/secondBrainTrace.ts', + 'Priority file previews:', + 'File: package.json', + '{"name":"connectai"}', + 'File: src/agent.ts', + 'export class AgentExecutor {}' + ].join('\n'); + + const brief = agent.buildJarvisProjectBriefContext( + '그럼 이 프로젝트에 대한 설계, 아키텍처는 어떤거 같아?', + localPathContext, + '' + ); + + expect(brief).toContain('[JARVIS PROJECT BRIEF]'); + expect(brief).toContain('/Volumes/Data/project/Antigravity/ConnectAI'); + expect(brief).toContain('src/agent.ts'); + expect(brief).toContain('Thinking partner response contract'); + expect(brief).toContain('direct verdict'); + expect(brief).toContain('decision fork'); + }); });