Version 2.55.0 Release: Rebranding to Astra

This commit is contained in:
g1nation
2026-05-03 20:40:40 +09:00
parent d8ae0b5964
commit a5f3e383d4
19 changed files with 140 additions and 67 deletions
+58 -10
View File
@@ -207,7 +207,7 @@ export class AgentExecutor {
// Decide whether to use Multi-Agent Workflow (for complex requests)
const isComplex = prompt && (prompt.length > 100 || /(분석|보고서|설계|기획|정리)/.test(prompt));
if (isComplex && loopDepth === 0 && multiAgentEnabled) {
if (isComplex && loopDepth === 0 && multiAgentEnabled && !this.isAstraModeArchitectureQuestion(prompt)) {
return this.executeMultiAgentWorkflow(prompt!, modelName, options);
}
@@ -293,6 +293,12 @@ export class AgentExecutor {
if (projectBriefContext) {
contextBlock += `\n\n${projectBriefContext}`;
}
const modeArchitectureContext = prompt && loopDepth === 0
? this.buildAstraModeArchitectureContext(prompt)
: '';
if (modeArchitectureContext) {
contextBlock += `\n\n${modeArchitectureContext}`;
}
// 2. Setup History
if (prompt !== null) {
@@ -792,6 +798,40 @@ export class AgentExecutor {
return /(어떤\s*거?\s*같|어때|어떻게\s*생각|의견|판단|방향|설계|아키텍처|구조|자비스|생각.*정리|갈림길|architecture|design|direction|opinion|think|judge)/i.test(prompt);
}
private isAstraModeArchitectureQuestion(prompt: string): boolean {
const mentionsGuard = /\bguard\b|가드|Guard|Chronicle Guard|Project Chronicle/i.test(prompt);
const mentionsMultiAgent = /\bMA\b|multi[-\s]?agent|멀티\s*에이전트|다중\s*에이전트|Planner|Researcher|Writer/i.test(prompt);
const asksDecision = /(분리|통합|모드|사용|좋을까|맞을까|구조|설계|아키텍처|의견|판단|어때|어떤\s*거?\s*같|separate|combine|mode|architecture|design|opinion)/i.test(prompt);
return asksDecision && mentionsGuard && mentionsMultiAgent;
}
private buildAstraModeArchitectureContext(prompt: string): string {
if (!this.isAstraModeArchitectureQuestion(prompt)) {
return '';
}
return [
'[ASTRA MODE ARCHITECTURE DECISION CONTEXT]',
'The user is asking about Astra itself, specifically whether Guard mode and MA/Multi-Agent mode should remain separate.',
'',
'Confirmed implementation facts from the current codebase:',
'- Guard is currently exposed as a sidebar toggle, but it defaults to enabled in the webview UI.',
'- Guard context is built by buildProjectChronicleGuardContext(activeProject) and passed into AgentExecutor as designerContext.',
'- In the normal single-agent path, designerContext is injected into the system prompt as [PROJECT CHRONICLE GUARD].',
'- In the Multi-Agent path, designerContext is appended as Project Chronicle Guard context for the workflow manager.',
'- Multi-Agent is controlled by the g1nation.multiAgentEnabled config and is only used for complex prompts.',
'- Current risk: when Multi-Agent starts, it returns early before the normal path builds local project preflight, Second Brain Trace, recent project knowledge context, and thinking-partner context.',
'',
'Product decision guidance:',
'- Do not treat Guard and MA as two equal user-facing modes.',
'- Guard should be an always-on policy/context layer: project target, evidence discipline, record hygiene, tone, and decision logging.',
'- MA should be an optional execution strategy chosen automatically for genuinely complex tasks.',
'- Recommended UX: hide or de-emphasize the Guard toggle, show it as Auto/On by default, and let Astra route between single-agent and MA internally.',
'- Recommended answer: give a clear verdict that separating them as peer modes is not ideal; separate them internally by responsibility instead.',
'- Mention the concrete risk that MA can currently bypass richer context assembly, so unifying the context preparation before routing is the next engineering step.'
].join('\n');
}
private buildJarvisProjectBriefContext(prompt: string, localPathContext: string, recentProjectKnowledgeContext: string): string {
if (!this.isThinkingPartnerRequest(prompt)) {
return '';
@@ -1102,6 +1142,7 @@ export class AgentExecutor {
private buildProjectKnowledgeFallbackAnswer(localPathContext: string, record?: { filePath: string; relativePath: string } | null): string {
const pathMatch = localPathContext.match(/Path:\s*(.+)/);
const projectPath = pathMatch?.[1]?.trim() || '제공된 로컬 프로젝트 경로';
const projectDisplayName = this.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 = this.extractPriorityPreviewFiles(localPathContext).slice(0, 10);
@@ -1123,10 +1164,10 @@ export class AgentExecutor {
'',
'## 바로 만들 지식 초안',
'```markdown',
'# ConnectAI Project Knowledge Overview',
`# ${projectDisplayName} Project Knowledge Overview`,
'',
'## Purpose',
'ConnectAI는 VS Code 안에서 로컬 AI 에이전트, Second Brain, 프로젝트 기록, 에이전트 스킬을 연결하는 개발 보조 프로젝트다.',
`${projectDisplayName}는 VS Code 안에서 로컬 AI 에이전트, Second Brain, 프로젝트 기록, 에이전트 스킬을 연결하는 개발 보조 프로젝트다.`,
'',
'## Confirmed Structure',
'- `src/agent.ts`: 에이전트 실행, 로컬 경로 프리플라이트, Second Brain Trace, 액션 실행 흐름의 중심.',
@@ -1140,7 +1181,7 @@ export class AgentExecutor {
'- 전체 아키텍처는 파일 구조와 일부 프리뷰 기준으로 파악 가능하지만, 세부 동작 지식은 `src/agent.ts`, `src/sidebarProvider.ts`, `secondBrainTrace.ts`, `projectChronicle` 순서로 심화 분석해 보강해야 한다.',
'',
'## Recommended Next Record',
'- `docs/records/ConnectAI/development/YYYY-MM-DD_connectai_project_knowledge_overview.md`',
`- \`docs/records/${path.basename(projectPath)}/development/YYYY-MM-DD_${projectDisplayName.toLowerCase()}_project_knowledge_overview.md\``,
'```',
'',
'## 다음 액션',
@@ -1170,8 +1211,9 @@ export class AgentExecutor {
try {
const projectName = path.basename(projectPath);
const projectDisplayName = this.getProjectDisplayName(projectPath);
const today = new Date().toISOString().slice(0, 10);
const slug = projectName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'project';
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 });
@@ -1187,19 +1229,20 @@ export class AgentExecutor {
const pathMatch = localPathContext.match(/Path:\s*(.+)/);
const projectPath = pathMatch?.[1]?.trim() || 'Unknown project path';
const projectName = path.basename(projectPath);
const projectDisplayName = this.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 = this.extractPriorityPreviewFiles(localPathContext);
return [
`# ${projectName} Project Knowledge Overview`,
`# ${projectDisplayName} Project Knowledge Overview`,
'',
`Date: ${new Date().toISOString()}`,
`Project: ${projectName}`,
`Project: ${projectDisplayName}`,
`Repository: \`${projectPath}\``,
'',
'## Purpose',
`${projectName}는 VS Code 안에서 로컬 AI 에이전트, Second Brain, 프로젝트 기록, 에이전트 스킬을 연결하는 개발 보조 프로젝트다.`,
`${projectDisplayName}는 VS Code 안에서 로컬 AI 에이전트, Second Brain, 프로젝트 기록, 에이전트 스킬을 연결하는 개발 보조 프로젝트다.`,
'',
'## Confirmed Structure',
'- `src/agent.ts`: 에이전트 실행, 로컬 경로 프리플라이트, Second Brain Trace, 액션 실행 흐름의 중심.',
@@ -1227,6 +1270,11 @@ export class AgentExecutor {
].join('\n');
}
private getProjectDisplayName(projectPath: string): string {
const projectName = path.basename(projectPath);
return /^connectai$/i.test(projectName) ? 'Astra' : projectName;
}
private listProjectTree(root: string, current: string, depth: number, maxDepth: number, limit: number): string {
if (limit <= 0 || depth > maxDepth) {
return '';
@@ -1646,7 +1694,7 @@ export class AgentExecutor {
const cmd = match[1].trim();
try {
const safeCmd = sanitizeCommand(cmd);
const terminal = vscode.window.terminals.find(t => t.name === 'G1nation Terminal') || vscode.window.createTerminal({ name: 'G1nation Terminal', cwd: rootPath });
const terminal = vscode.window.terminals.find(t => t.name === 'Astra Terminal') || vscode.window.createTerminal({ name: 'Astra Terminal', cwd: rootPath });
terminal.show();
terminal.sendText(safeCmd);
report.push(`🚀 Executed: ${safeCmd}`);
@@ -1766,7 +1814,7 @@ export class AgentExecutor {
try {
const { execSync } = require('child_process');
execSync(`git add .`, { cwd: brainDir });
execSync(`git commit -m "[G1nation] Knowledge Update"`, { cwd: brainDir });
execSync(`git commit -m "[Astra] Knowledge Update"`, { cwd: brainDir });
execSync(`git push`, { cwd: brainDir });
} catch (err) {
logError('Second Brain sync failed.', err);
+1 -1
View File
@@ -20,7 +20,7 @@ export interface BridgeInterface {
/**
* BridgeServer:
* 외부 툴(EZER, A.U 등)과 G1nation 확장을 연결하는 통신 브릿지.
* 외부 툴(EZER, A.U 등)과 Astra 확장을 연결하는 통신 브릿지.
* 서비스 레이어(AIService, BrainService)를 통해 비즈니스 로직을 분리하여 유지보수성을 극대화했습니다.
*/
export class BridgeServer {
+1 -1
View File
@@ -44,7 +44,7 @@ export class HealthCheckMonitor {
if (reports.length > 0) {
logWarn(`Health Check Warnings: ${reports.join(' | ')}`);
vscode.window.showWarningMessage(`ConnectAI Health Warning: ${reports[0]}`);
vscode.window.showWarningMessage(`Astra Health Warning: ${reports[0]}`);
}
return {
+1 -1
View File
@@ -43,7 +43,7 @@ export class StatusBarManager {
break;
}
this.statusBarItem.text = `${icon} G1nation: ${status}`;
this.statusBarItem.text = `${icon} Astra: ${status}`;
this.statusBarItem.tooltip = detail || `Current State: ${status}`;
if (status === AgentStatus.Success || status === AgentStatus.Error) {
+5 -5
View File
@@ -18,10 +18,10 @@ import { SidebarChatProvider } from './sidebarProvider';
import { HealthCheckMonitor } from './core/health';
/**
* G1nation Extension Entry Point
* Astra Extension Entry Point
*/
export async function activate(context: vscode.ExtensionContext) {
logInfo('ConnectAI activating...');
logInfo('Astra activating...');
// Start Environment Health Monitoring
HealthCheckMonitor.runAllChecks();
@@ -30,7 +30,7 @@ export async function activate(context: vscode.ExtensionContext) {
// 0. Validate Configuration
const validation = validateConfig();
if (!validation.valid) {
vscode.window.showErrorMessage(`G1nation Configuration Error: ${validation.errors.join(' ')}`);
vscode.window.showErrorMessage(`Astra Configuration Error: ${validation.errors.join(' ')}`);
logError('Configuration validation failed.', { errors: validation.errors });
}
@@ -144,12 +144,12 @@ async function _ensureBrainDir(context: vscode.ExtensionContext): Promise<string
try {
fs.mkdirSync(defaultDir, { recursive: true });
// Create a welcome file
fs.writeFileSync(path.join(defaultDir, 'Welcome.md'), "# Welcome to your Second Brain\n\nG1nation will store and retrieve knowledge from here.");
fs.writeFileSync(path.join(defaultDir, 'Welcome.md'), "# Welcome to your Second Brain\n\nAstra will store and retrieve knowledge from here.");
} catch (e) {}
}
return defaultDir;
}
/**
* G1nation Extension Entry Point
* Astra Extension Entry Point
*/
@@ -38,6 +38,10 @@ export function buildProjectChronicleGuardContext(project: ProjectProfile | null
'- Do not mark a decision as accepted until the user confirms it.',
'- Before confirmation, call decisions "candidates" or "pending".',
'- Prefer "reduced adoption" when the idea is useful but too large for the MVP.',
'- For Astra mode-design questions, do not treat Guard and MA/Multi-Agent as two equal user-facing modes.',
'- Treat Guard as a policy/context layer for evidence, project target, record hygiene, and thinking-partner tone.',
'- Treat MA/Multi-Agent as an execution strategy that may be selected automatically for complex tasks.',
'- If asked whether Guard and MA should be separated, recommend internal responsibility separation but a simpler Auto user experience.',
'',
'Evidence policy:',
'- No Evidence, No Project Claim: do not state that the current project has a technical structure unless it is supported by user-provided facts, source code, design docs, project docs, or project records.',
+13 -13
View File
@@ -626,7 +626,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
if (!name) return;
const description = await vscode.window.showInputBox({
prompt: 'Optional description shown in the G1nation sidebar',
prompt: 'Optional description shown in the Astra sidebar',
value: ''
});
@@ -687,7 +687,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
if (!folder) return;
const description = await vscode.window.showInputBox({
prompt: 'Edit optional description shown in the G1nation sidebar',
prompt: 'Edit optional description shown in the Astra sidebar',
value: target.description || ''
});
@@ -819,7 +819,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
`title: "${this._escapeYamlString(this._summarizeForTitle(firstUserMessage))}"`,
`category: "${this._escapeYamlString(meta.category)}"`,
`created_at: "${meta.createdAt}"`,
`source: "ConnectAI conversation"`,
`source: "Astra conversation"`,
`brain: "${this._escapeYamlString(meta.activeBrainName)}"`,
'status: raw',
'---',
@@ -874,7 +874,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
private _summarizeForTitle(value: string): string {
const normalized = value.replace(/\s+/g, ' ').trim();
if (!normalized) return 'ConnectAI Conversation Raw Data';
if (!normalized) return 'Astra Conversation Raw Data';
return normalized.length > 80 ? `${normalized.slice(0, 80)}...` : normalized;
}
@@ -930,7 +930,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
}
vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "G1nation: Syncing Second Brain...",
title: "Astra: Syncing Second Brain...",
cancellable: false
}, async () => {
try {
@@ -1573,7 +1573,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
this._chronicle.appendTimeline(profile, [`Auto ${recordType} record created: ${result.relativePath}`], createdAt);
await this._context.globalState.update(SidebarChatProvider.lastAutoChronicleSignatureStateKey, signature);
await this._sendChronicleRecords();
vscode.window.setStatusBarMessage(`G1nation: Chronicle auto-saved ${recordType}`, 3500);
vscode.window.setStatusBarMessage(`Astra: Chronicle auto-saved ${recordType}`, 3500);
} catch (err: any) {
logError('Automatic Chronicle record write failed.', { error: err?.message || String(err), recordType });
}
@@ -1944,7 +1944,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>G1nation</title>
<title>Astra</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
:root {
@@ -2558,7 +2558,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
<body>
<div class="header">
<div class="header-top">
<div class="brand"><div class="logo">✦</div> G1nation</div>
<div class="brand"><div class="logo">✦</div> Astra</div>
<div class="header-actions">
<button class="icon-btn" id="newChatBtn" data-tooltip="New Chat">New</button>
<button class="icon-btn" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">Wiki</button>
@@ -2639,7 +2639,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
<div class="chat" id="chat">
<div class="welcome">
<div class="welcome-logo">✦</div>
<div class="welcome-title">Welcome to G1nation</div>
<div class="welcome-title">Welcome to Astra</div>
<p>Your premium local AI assistant.<br>Ready to analyze projects and build reports.</p>
</div>
</div>
@@ -2686,7 +2686,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
// [State Persistence - Tier 0] 즉시 복원 (Instant Restore from WebView State)
const previousState = vscode.getState();
if (previousState && previousState.history && previousState.history.length > 0) {
console.log('[G1nation] Restoring from Webview State...');
console.log('[Astra] Restoring from Webview State...');
renderHistory(previousState.history);
}
@@ -2899,7 +2899,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
const head = document.createElement('div');
head.className = 'msg-head';
head.innerHTML = isUser ? '<div class="av av-user">U</div> You' : '<div class="av av-ai">✦</div> G1nation';
head.innerHTML = isUser ? '<div class="av av-user">U</div> You' : '<div class="av av-ai">✦</div> Astra';
const body = document.createElement('div');
body.className = 'msg-body markdown-body';
@@ -2985,7 +2985,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
historyOverlay.classList.remove('visible');
break;
case 'clearChat':
chat.innerHTML = '<div class="welcome"><div class="welcome-logo">✦</div><div class="welcome-title">Welcome to G1nation</div><p>Your premium local AI assistant.<br>Ready to analyze projects and build reports.</p></div>';
chat.innerHTML = '<div class="welcome"><div class="welcome-logo">✦</div><div class="welcome-title">Welcome to Astra</div><p>Your premium local AI assistant.<br>Ready to analyze projects and build reports.</p></div>';
break;
case 'focusInput':
input.focus();
@@ -3325,7 +3325,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
try {
localStorage.setItem('g1nation_last_model', _selectedModel);
} catch(e) {
console.warn('[G1nation] LocalStorage 저장 실패:', e);
console.warn('[Astra] LocalStorage 저장 실패:', e);
}
// [State Persistence - Tier 1] VS Code 전역 설정에 동기화 (영구 저장)
vscode.postMessage({ type: 'model', value: _selectedModel });
+3 -2
View File
@@ -7,7 +7,7 @@ import { getConfig, BrainProfile, EXCLUDED_DIRS } from './config';
export type EngineKind = 'lmstudio' | 'ollama';
const outputChannel = vscode.window.createOutputChannel('Connect AI');
const outputChannel = vscode.window.createOutputChannel('Astra');
function timestamp() {
return new Date().toISOString();
@@ -137,7 +137,8 @@ export function findBrainFiles(dir: string): string[] {
return results;
}
const BASE_SYSTEM_PROMPT = `You are G1nation, also called Steve when the user asks your name.
const BASE_SYSTEM_PROMPT = `You are Astra, a Jarvis-style local project operating assistant.
If the user asks your name, say you are Astra.
Reply naturally in the user's language.
Core behavior: