Version 2.43.0 Release: Contextual Project Trace & Overrides
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "g1nation",
|
"name": "g1nation",
|
||||||
"displayName": "G1nation",
|
"displayName": "G1nation",
|
||||||
"description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.",
|
"description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.",
|
||||||
"version": "2.42.0",
|
"version": "2.43.0",
|
||||||
"publisher": "connectailab",
|
"publisher": "connectailab",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/icon.png",
|
"icon": "assets/icon.png",
|
||||||
|
|||||||
+1
-1
@@ -843,7 +843,7 @@ export class AgentExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isBlockingProjectKnowledgeAnswer(content: string): boolean {
|
private isBlockingProjectKnowledgeAnswer(content: string): boolean {
|
||||||
return /(블로킹 질문|어떤 기능 영역|어떤 부분.*먼저|어떤 기능이나 아키텍처|구체적인 방향|방향 설정이 필요|명확히 알려주시면|우선적으로 정리|최종 사용 목적|Question reason)/i.test(content);
|
return /(블로킹 질문|어떤 기능 영역|어떤 부분.*먼저|어떤 기능이나 아키텍처|구체적인 방향|방향 설정이 필요|명확히 알려주시면|우선적으로 정리|최종 사용 목적|Question reason|별도의 파일 기록.*생성되지|파일 기록이 생성되지|더 깊이 있는 분석.*지정|해당 기능.*지정하여 요청)/i.test(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildProjectKnowledgeFallbackAnswer(localPathContext: string): string {
|
private buildProjectKnowledgeFallbackAnswer(localPathContext: string): string {
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ export function buildSecondBrainTrace(userQuery: string, brainRoot: string, opti
|
|||||||
const files = findBrainFiles(brainRoot)
|
const files = findBrainFiles(brainRoot)
|
||||||
.filter((file) => includeRaw || !isRawConversationPath(path.relative(brainRoot, file)));
|
.filter((file) => includeRaw || !isRawConversationPath(path.relative(brainRoot, file)));
|
||||||
const terms = tokenize(retrievalQuery);
|
const terms = tokenize(retrievalQuery);
|
||||||
const scored = files.map((file) => scoreFile(file, brainRoot, terms, queryIntent))
|
const targetProject = inferTargetProject(query);
|
||||||
|
const scored = files.map((file) => scoreFile(file, brainRoot, terms, queryIntent, targetProject))
|
||||||
.filter((doc) => doc.score >= 0.25)
|
.filter((doc) => doc.score >= 0.25)
|
||||||
.sort((a, b) => b.score - a.score)
|
.sort((a, b) => b.score - a.score)
|
||||||
.slice(0, options.limit || 5);
|
.slice(0, options.limit || 5);
|
||||||
@@ -370,7 +371,15 @@ function tokenize(value: string): string[] {
|
|||||||
.filter((term) => term.length >= 2 && !stopWords.has(term));
|
.filter((term) => term.length >= 2 && !stopWords.has(term));
|
||||||
}
|
}
|
||||||
|
|
||||||
function scoreFile(file: string, brainRoot: string, terms: string[], intent: SecondBrainQueryIntent): SecondBrainTraceDocument {
|
function inferTargetProject(query: string): string | undefined {
|
||||||
|
const pathMatch = query.match(/\/Volumes\/Data\/project\/Antigravity\/([^\s`"'<>/]+)/i);
|
||||||
|
if (pathMatch?.[1]) return pathMatch[1].toLowerCase();
|
||||||
|
|
||||||
|
const namedProject = query.match(/\b(connectai|datacollector|skybound)\b/i);
|
||||||
|
return namedProject?.[1]?.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scoreFile(file: string, brainRoot: string, terms: string[], intent: SecondBrainQueryIntent, targetProject?: string): SecondBrainTraceDocument {
|
||||||
const relative = path.relative(brainRoot, file);
|
const relative = path.relative(brainRoot, file);
|
||||||
const title = path.basename(file, path.extname(file));
|
const title = path.basename(file, path.extname(file));
|
||||||
const basename = relative.toLowerCase();
|
const basename = relative.toLowerCase();
|
||||||
@@ -385,6 +394,9 @@ function scoreFile(file: string, brainRoot: string, terms: string[], intent: Sec
|
|||||||
|
|
||||||
const lower = content.toLowerCase();
|
const lower = content.toLowerCase();
|
||||||
let score = pathPriority(relative, intent);
|
let score = pathPriority(relative, intent);
|
||||||
|
if (targetProject) {
|
||||||
|
score += projectRelevanceScore(relative, lower, targetProject);
|
||||||
|
}
|
||||||
for (const term of terms) {
|
for (const term of terms) {
|
||||||
if (basename.includes(term)) score += 4;
|
if (basename.includes(term)) score += 4;
|
||||||
const matches = lower.split(term).length - 1;
|
const matches = lower.split(term).length - 1;
|
||||||
@@ -405,6 +417,22 @@ function scoreFile(file: string, brainRoot: string, terms: string[], intent: Sec
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function projectRelevanceScore(relativePath: string, lowerContent: string, targetProject: string): number {
|
||||||
|
const normalized = relativePath.toLowerCase();
|
||||||
|
let score = 0;
|
||||||
|
if (normalized.includes(targetProject)) score += 12;
|
||||||
|
const targetMatches = lowerContent.split(targetProject).length - 1;
|
||||||
|
if (targetMatches > 0) score += Math.min(targetMatches * 4, 20);
|
||||||
|
|
||||||
|
const otherProjectMatch = lowerContent.match(/(?:project|프로젝트|repository|repo)\s*[::]?\s*`?\/?volumes\/data\/project\/antigravity\/([a-z0-9_-]+)/i)
|
||||||
|
|| lowerContent.match(/(?:project|프로젝트)\s*[::]\s*([a-z0-9_-]+)/i);
|
||||||
|
const otherProject = otherProjectMatch?.[1]?.toLowerCase();
|
||||||
|
if (otherProject && otherProject !== targetProject) score -= 24;
|
||||||
|
|
||||||
|
if (normalized.includes('project_logs') && otherProject && otherProject !== targetProject) score -= 8;
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
function classifySourceType(relativePath: string, content: string): SecondBrainSourceType {
|
function classifySourceType(relativePath: string, content: string): SecondBrainSourceType {
|
||||||
const normalized = relativePath.toLowerCase();
|
const normalized = relativePath.toLowerCase();
|
||||||
const lower = content.toLowerCase();
|
const lower = content.toLowerCase();
|
||||||
|
|||||||
@@ -113,4 +113,16 @@ describe('local project path preflight', () => {
|
|||||||
expect(fallback).toContain('ConnectAI Project Knowledge Overview');
|
expect(fallback).toContain('ConnectAI Project Knowledge Overview');
|
||||||
expect(fallback).not.toContain('어떤 기능 영역을 가장 먼저');
|
expect(fallback).not.toContain('어떤 기능 영역을 가장 먼저');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('treats no-record-created answers as incomplete 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 = 'Record Path Check: 요청하신 지식 생성 작업은 파일 목록 검토를 통해 완료되었으며, 별도의 파일 기록이 생성되지 않았습니다.';
|
||||||
|
|
||||||
|
expect(agent.isBlockingProjectKnowledgeAnswer(answer)).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -215,4 +215,44 @@ describe('Second Brain Trace', () => {
|
|||||||
expect(trace.retrievedDocuments[0].path).not.toContain('API Gateway.md');
|
expect(trace.retrievedDocuments[0].path).not.toContain('API Gateway.md');
|
||||||
expect(renderSecondBrainTraceContext(trace)).toContain('Approval likelihood is an inference');
|
expect(renderSecondBrainTraceContext(trace)).toContain('Approval likelihood is an inference');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('prioritizes notes for the project named in a local Antigravity path', () => {
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'second-brain-project-target-'));
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path.join(root, 'Project_Logs'), { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(root, 'Project_Logs', '2026-04-25-Datacollector_Fix.md'),
|
||||||
|
[
|
||||||
|
'# Datacollector Fix',
|
||||||
|
'',
|
||||||
|
'Project: Datacollector',
|
||||||
|
'Repository: `/Volumes/Data/project/Antigravity/Datacollector`',
|
||||||
|
'project antigravity repository documentation knowledge'
|
||||||
|
].join('\n'),
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(root, 'Project_Logs', '2026-05-02-ConnectAI_Project_Knowledge.md'),
|
||||||
|
[
|
||||||
|
'# ConnectAI Project Knowledge',
|
||||||
|
'',
|
||||||
|
'Project: ConnectAI',
|
||||||
|
'Repository: `/Volumes/Data/project/Antigravity/ConnectAI`',
|
||||||
|
'ConnectAI project knowledge for local AI assistant, Second Brain Trace, and Project Chronicle.'
|
||||||
|
].join('\n'),
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
const trace = buildSecondBrainTrace(
|
||||||
|
'그러면 지금 /Volumes/Data/project/Antigravity/ConnectAI 프로젝트에 대한 지식을 만들어',
|
||||||
|
root,
|
||||||
|
{ force: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(trace.retrievedDocuments[0].path).toContain('ConnectAI_Project_Knowledge.md');
|
||||||
|
expect(trace.retrievedDocuments[0].path).not.toContain('Datacollector');
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user