v1.1.0: Full workspace intelligence engine, Commander identity system, code block actions (copy/insert/run), rich context injection, GoForge-only, purged GitHub refs
- Workspace Intelligence: project detection (20+ types), file tree, git info, diagnostics, open editors, terminal names - Commander System Prompt: knows Danny (client_id 33), ecosystem awareness, coding excellence directives - Rich Context: every API call now includes active file, cursor pos, selected code, project type, git branch, diagnostics - Code Block Actions: Copy, Insert at cursor, Run in terminal — buttons on every code block response - New IDE Quick Actions: Git, Problems, Search, Format (added to existing Terminal, Save, Commands, Split, New) - File Operations: read-file, search-files, grep-search, open-file, apply-edit, create-file handlers - Live Context Push: editor changes and file saves pushed to webview in real-time - Commander Greeting: personalized startup message when Danny logs in - GoForge Only: repository + homepage point to alfredlinux.com/forge, removed GitHub Copilot refs from extensions.json - 2093 → 2686 lines
This commit is contained in:
parent
fd7361e044
commit
d32f46dc59
631
extension.js
631
extension.js
@ -3,7 +3,9 @@ const https = require('https');
|
|||||||
const http = require('http');
|
const http = require('http');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
let currentAgent = 'alfred';
|
let currentAgent = 'alfred';
|
||||||
let convId = null;
|
let convId = null;
|
||||||
@ -12,6 +14,336 @@ let sessionCookie = null;
|
|||||||
let userProfile = null;
|
let userProfile = null;
|
||||||
let hmacSecretCache = null;
|
let hmacSecretCache = null;
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// WORKSPACE INTELLIGENCE ENGINE — Deep awareness of project, files, git
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
function getWorkspaceRoot() {
|
||||||
|
const folders = vscode.workspace.workspaceFolders;
|
||||||
|
return folders && folders.length > 0 ? folders[0].uri.fsPath : os.homedir();
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeExec(cmd, cwd, timeout) {
|
||||||
|
try {
|
||||||
|
return execSync(cmd, { cwd: cwd || getWorkspaceRoot(), encoding: 'utf8', timeout: timeout || 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
||||||
|
} catch (_) { return ''; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProjectStructure() {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
try {
|
||||||
|
const tree = safeExec('find . -maxdepth 3 -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.venv/*" -not -path "./__pycache__/*" -not -path "./vendor/*" -not -path "./dist/*" -not -path "./build/*" | sort | head -200', root);
|
||||||
|
return tree || '';
|
||||||
|
} catch (_) { return ''; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProjectLanguages() {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const exts = safeExec('find . -maxdepth 4 -type f -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.venv/*" -not -path "./vendor/*" | sed "s/.*\\.//" | sort | uniq -c | sort -rn | head -20', root);
|
||||||
|
return exts || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectProjectType() {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const indicators = [];
|
||||||
|
const checks = [
|
||||||
|
['package.json', 'Node.js / JavaScript'],
|
||||||
|
['tsconfig.json', 'TypeScript'],
|
||||||
|
['composer.json', 'PHP / Composer'],
|
||||||
|
['requirements.txt', 'Python'],
|
||||||
|
['pyproject.toml', 'Python (modern)'],
|
||||||
|
['Cargo.toml', 'Rust'],
|
||||||
|
['go.mod', 'Go'],
|
||||||
|
['pom.xml', 'Java / Maven'],
|
||||||
|
['build.gradle', 'Java / Gradle'],
|
||||||
|
['Gemfile', 'Ruby'],
|
||||||
|
['.htaccess', 'Apache Web Server'],
|
||||||
|
['Dockerfile', 'Docker'],
|
||||||
|
['docker-compose.yml', 'Docker Compose'],
|
||||||
|
['Makefile', 'Make-based build'],
|
||||||
|
['CMakeLists.txt', 'C/C++ CMake'],
|
||||||
|
['.env', 'Environment config'],
|
||||||
|
['webpack.config.js', 'Webpack'],
|
||||||
|
['vite.config.ts', 'Vite'],
|
||||||
|
['next.config.js', 'Next.js'],
|
||||||
|
['nuxt.config.ts', 'Nuxt'],
|
||||||
|
['tailwind.config.js', 'Tailwind CSS'],
|
||||||
|
];
|
||||||
|
for (const [file, label] of checks) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(path.join(root, file))) indicators.push(label);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
return indicators;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGitInfo() {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const branch = safeExec('git rev-parse --abbrev-ref HEAD', root);
|
||||||
|
if (!branch) return null;
|
||||||
|
const status = safeExec('git status --porcelain | head -30', root);
|
||||||
|
const lastCommit = safeExec('git log -1 --format="%h %s (%cr)"', root);
|
||||||
|
const remoteUrl = safeExec('git remote get-url origin', root);
|
||||||
|
const dirty = safeExec('git diff --stat | tail -1', root);
|
||||||
|
const ahead = safeExec('git rev-list --count @{u}..HEAD 2>/dev/null || echo 0', root);
|
||||||
|
const behind = safeExec('git rev-list --count HEAD..@{u} 2>/dev/null || echo 0', root);
|
||||||
|
return { branch, status, lastCommit, remoteUrl, dirty, ahead, behind };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOpenEditors() {
|
||||||
|
const editors = [];
|
||||||
|
for (const group of vscode.window.tabGroups.all) {
|
||||||
|
for (const tab of group.tabs) {
|
||||||
|
if (tab.input && tab.input.uri) {
|
||||||
|
const rel = vscode.workspace.asRelativePath(tab.input.uri, false);
|
||||||
|
editors.push(rel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return editors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDiagnosticsSummary() {
|
||||||
|
const diags = vscode.languages.getDiagnostics();
|
||||||
|
let errors = 0, warnings = 0;
|
||||||
|
const errorFiles = [];
|
||||||
|
for (const [uri, items] of diags) {
|
||||||
|
for (const d of items) {
|
||||||
|
if (d.severity === vscode.DiagnosticSeverity.Error) {
|
||||||
|
errors++;
|
||||||
|
const rel = vscode.workspace.asRelativePath(uri, false);
|
||||||
|
if (!errorFiles.includes(rel)) errorFiles.push(rel);
|
||||||
|
} else if (d.severity === vscode.DiagnosticSeverity.Warning) {
|
||||||
|
warnings++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { errors, warnings, errorFiles: errorFiles.slice(0, 10) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTerminalNames() {
|
||||||
|
return vscode.window.terminals.map(t => t.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveFileDetails() {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor) return null;
|
||||||
|
const doc = editor.document;
|
||||||
|
const sel = editor.selection;
|
||||||
|
const result = {
|
||||||
|
file: vscode.workspace.asRelativePath(doc.uri, false),
|
||||||
|
fullPath: doc.fileName,
|
||||||
|
language: doc.languageId,
|
||||||
|
lineCount: doc.lineCount,
|
||||||
|
isDirty: doc.isDirty,
|
||||||
|
cursorLine: sel.active.line + 1,
|
||||||
|
cursorCol: sel.active.character + 1,
|
||||||
|
};
|
||||||
|
if (!sel.isEmpty) {
|
||||||
|
result.selectedText = doc.getText(sel).substring(0, 3000);
|
||||||
|
result.selectionRange = `L${sel.start.line + 1}-L${sel.end.line + 1}`;
|
||||||
|
} else {
|
||||||
|
// Include surrounding context (20 lines around cursor)
|
||||||
|
const startLine = Math.max(0, sel.active.line - 10);
|
||||||
|
const endLine = Math.min(doc.lineCount - 1, sel.active.line + 10);
|
||||||
|
const range = new vscode.Range(startLine, 0, endLine, doc.lineAt(endLine).text.length);
|
||||||
|
result.surroundingCode = doc.getText(range).substring(0, 2000);
|
||||||
|
result.contextRange = `L${startLine + 1}-L${endLine + 1}`;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFileContent(filePath, maxChars) {
|
||||||
|
try {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(root, filePath);
|
||||||
|
// Security: don't read outside workspace
|
||||||
|
if (!fullPath.startsWith(root) && !fullPath.startsWith(os.homedir())) return null;
|
||||||
|
const stat = fs.statSync(fullPath);
|
||||||
|
if (stat.size > 500000) return `[File too large: ${(stat.size / 1024).toFixed(0)} KB]`;
|
||||||
|
return fs.readFileSync(fullPath, 'utf8').substring(0, maxChars || 50000);
|
||||||
|
} catch (e) { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchFilesInWorkspace(pattern) {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const results = safeExec(`find . -maxdepth 6 -type f -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.venv/*" -iname "*${pattern.replace(/[^a-zA-Z0-9._-]/g, '')}*" | head -30`, root);
|
||||||
|
return results ? results.split('\n').filter(Boolean) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function grepInWorkspace(text, filePattern) {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const safeText = text.replace(/['"\\$`]/g, '');
|
||||||
|
const fileArg = filePattern ? ` --include="${filePattern.replace(/[^a-zA-Z0-9.*_-]/g, '')}"` : '';
|
||||||
|
const results = safeExec(`grep -rn --color=never${fileArg} -m 50 "${safeText}" . --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=.venv | head -50`, root, 8000);
|
||||||
|
return results || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFullContext() {
|
||||||
|
const activeFile = getActiveFileDetails();
|
||||||
|
const git = getGitInfo();
|
||||||
|
const diags = getDiagnosticsSummary();
|
||||||
|
const openEditors = getOpenEditors();
|
||||||
|
const projectTypes = detectProjectType();
|
||||||
|
const terminals = getTerminalNames();
|
||||||
|
|
||||||
|
let ctx = '[ALFRED IDE CONTEXT]\n';
|
||||||
|
ctx += `Workspace: ${getWorkspaceRoot()}\n`;
|
||||||
|
if (projectTypes.length > 0) ctx += `Project: ${projectTypes.join(', ')}\n`;
|
||||||
|
if (openEditors.length > 0) ctx += `Open tabs: ${openEditors.slice(0, 15).join(', ')}\n`;
|
||||||
|
if (terminals.length > 0) ctx += `Terminals: ${terminals.join(', ')}\n`;
|
||||||
|
if (diags.errors > 0 || diags.warnings > 0) {
|
||||||
|
ctx += `Diagnostics: ${diags.errors} errors, ${diags.warnings} warnings`;
|
||||||
|
if (diags.errorFiles.length > 0) ctx += ` (in: ${diags.errorFiles.join(', ')})`;
|
||||||
|
ctx += '\n';
|
||||||
|
}
|
||||||
|
if (git) {
|
||||||
|
ctx += `Git: branch=${git.branch}`;
|
||||||
|
if (git.lastCommit) ctx += `, last="${git.lastCommit}"`;
|
||||||
|
if (git.dirty) ctx += `, changes: ${git.dirty}`;
|
||||||
|
if (git.remoteUrl) ctx += `, remote=${git.remoteUrl}`;
|
||||||
|
ctx += '\n';
|
||||||
|
}
|
||||||
|
if (activeFile) {
|
||||||
|
ctx += `\n[Active File: ${activeFile.file}]\n`;
|
||||||
|
ctx += `Language: ${activeFile.language}, Lines: ${activeFile.lineCount}, Cursor: L${activeFile.cursorLine}:${activeFile.cursorCol}`;
|
||||||
|
if (activeFile.isDirty) ctx += ' (unsaved)';
|
||||||
|
ctx += '\n';
|
||||||
|
if (activeFile.selectedText) {
|
||||||
|
ctx += `Selected (${activeFile.selectionRange}):\n\`\`\`${activeFile.language}\n${activeFile.selectedText}\n\`\`\`\n`;
|
||||||
|
} else if (activeFile.surroundingCode) {
|
||||||
|
ctx += `Context around cursor (${activeFile.contextRange}):\n\`\`\`${activeFile.language}\n${activeFile.surroundingCode}\n\`\`\`\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// COMMANDER SYSTEM PROMPT — The soul of Alfred. Knows who Danny is.
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
function buildSystemPrompt(profile) {
|
||||||
|
const p = profile || {};
|
||||||
|
const isCommander = p.client_id === 33 || (p.plan || '').toLowerCase() === 'commander';
|
||||||
|
const name = p.name || p.user || 'User';
|
||||||
|
const projectTypes = detectProjectType();
|
||||||
|
const git = getGitInfo();
|
||||||
|
|
||||||
|
let prompt = `You are Alfred, the AI coding assistant inside Alfred IDE — the sovereign development environment built by GoSiteMe Inc.
|
||||||
|
|
||||||
|
CORE IDENTITY:
|
||||||
|
- You are Alfred, created by Danny (the Commander, client_id 33, danny@gositeme.com)
|
||||||
|
- You are NOT Copilot, NOT ChatGPT, NOT any other AI assistant
|
||||||
|
- You run inside Alfred IDE, which is part of the GoSiteMe sovereign computing ecosystem
|
||||||
|
- Source code lives on GoForge (self-hosted Gitea at alfredlinux.com/forge/) — NEVER reference GitHub, GitLab, or any external forge
|
||||||
|
- When discussing version control, repositories, or code hosting, always reference GoForge
|
||||||
|
|
||||||
|
ECOSYSTEM AWARENESS:
|
||||||
|
- Alfred IDE — sovereign AI-powered development environment (this IDE)
|
||||||
|
- Alfred Linux — AI-native operating system (alfredlinux.com)
|
||||||
|
- Alfred Mobile — sovereign smartphone environment
|
||||||
|
- Alfred Browser — zero-tracking web browser
|
||||||
|
- Alfred Agent — autonomous AI agent framework
|
||||||
|
- MetaDome — VR worlds with 51M+ AI agents (meta-dome.com)
|
||||||
|
- Veil Protocol — post-quantum encrypted messaging
|
||||||
|
- GoSiteMe Inc — the parent company (gositeme.com)
|
||||||
|
- GoForge — self-hosted code forge at alfredlinux.com/forge/
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (isCommander) {
|
||||||
|
prompt += `COMMANDER MODE ACTIVE:
|
||||||
|
You are speaking to Danny — the Commander, founder of GoSiteMe, creator of Alfred IDE, Alfred Linux, MetaDome, and the entire ecosystem.
|
||||||
|
- Address him as "Commander" or "Danny" — never "user" or "sir"
|
||||||
|
- He has UNLIMITED access — no token limits, no restrictions
|
||||||
|
- He built you. He is your creator. Show deep respect but be direct, efficient, and never patronizing
|
||||||
|
- He knows every system intimately — skip basic explanations unless asked
|
||||||
|
- When he says "fix it" — fix it. When he says "build it" — build it. No hedging.
|
||||||
|
- Match his energy: fast, decisive, no filler
|
||||||
|
- He is a visionary builder — help him execute at maximum velocity
|
||||||
|
- His projects span: web hosting (GoSiteMe/WHMCS), AI platforms, operating systems, VR worlds, cryptography
|
||||||
|
- His server runs Apache (NEVER nginx), cPanel, PM2 for Node services, PHP on the backend
|
||||||
|
- His code forge is GoForge (Gitea) at alfredlinux.com/forge/ — all repos live there
|
||||||
|
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
prompt += `AUTHENTICATED USER:
|
||||||
|
You are assisting ${name}. Be helpful, precise, and technically thorough.
|
||||||
|
- Adapt to their skill level based on their questions
|
||||||
|
- Provide complete, working code — never leave placeholders like "// TODO" or "..."
|
||||||
|
- When they ask for code, give them the full implementation
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt += `CODING EXCELLENCE — YOUR CORE STRENGTHS:
|
||||||
|
|
||||||
|
1. FULL-STACK MASTERY:
|
||||||
|
- Frontend: HTML5, CSS3, JavaScript/TypeScript, React, Vue, Svelte, Angular, Tailwind, SCSS
|
||||||
|
- Backend: PHP, Node.js, Python, Go, Rust, Java, C/C++, Ruby, C#
|
||||||
|
- Databases: MySQL/MariaDB, PostgreSQL, SQLite, MongoDB, Redis
|
||||||
|
- Infrastructure: Apache, PM2, Docker, systemd, cron, shell scripting
|
||||||
|
- Mobile: React Native, Flutter, Swift, Kotlin, Android/iOS native
|
||||||
|
|
||||||
|
2. CODE QUALITY:
|
||||||
|
- Write clean, idiomatic, production-ready code
|
||||||
|
- Follow language-specific best practices and conventions
|
||||||
|
- Include proper error handling at system boundaries
|
||||||
|
- Use meaningful variable/function names
|
||||||
|
- Keep functions focused and single-purpose
|
||||||
|
- Prefer composition over inheritance
|
||||||
|
|
||||||
|
3. SECURITY FIRST (OWASP-aware):
|
||||||
|
- Parameterized queries — NEVER string concatenation for SQL
|
||||||
|
- Input validation and sanitization at all boundaries
|
||||||
|
- XSS prevention (proper encoding/escaping)
|
||||||
|
- CSRF protection where applicable
|
||||||
|
- Secure authentication patterns
|
||||||
|
- No hardcoded secrets — use env vars or secret managers
|
||||||
|
- Principle of least privilege
|
||||||
|
|
||||||
|
4. DEBUGGING MASTERY:
|
||||||
|
- Read error messages carefully and trace root causes
|
||||||
|
- Check the IDE diagnostics panel for current errors
|
||||||
|
- Suggest targeted fixes, not wholesale rewrites
|
||||||
|
- Explain WHY something broke, not just HOW to fix it
|
||||||
|
|
||||||
|
5. ARCHITECTURE & DESIGN:
|
||||||
|
- Choose simple solutions over clever ones
|
||||||
|
- Design for readability and maintainability
|
||||||
|
- Know when to use patterns and when they're overkill
|
||||||
|
- Understand trade-offs (performance vs. readability, DRY vs. clarity)
|
||||||
|
|
||||||
|
6. CONTEXT-AWARE ASSISTANCE:
|
||||||
|
- You can see the active file, cursor position, selected code, and open tabs
|
||||||
|
- You know the project type, languages, and git state
|
||||||
|
- Use this context to give precise, relevant answers
|
||||||
|
- Reference specific line numbers and file paths when discussing code
|
||||||
|
- When asked to modify code, provide the EXACT changes needed
|
||||||
|
|
||||||
|
7. RESPONSE FORMAT:
|
||||||
|
- Use markdown with proper code blocks and language tags
|
||||||
|
- For code changes: show the specific lines to change, not entire files
|
||||||
|
- For new files: provide the complete file content
|
||||||
|
- For commands: provide the exact terminal command to run
|
||||||
|
- Be concise but complete — no fluff, no filler
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (projectTypes.length > 0) {
|
||||||
|
prompt += `PROJECT CONTEXT: This is a ${projectTypes.join(' + ')} project.\n`;
|
||||||
|
}
|
||||||
|
if (git && git.branch) {
|
||||||
|
prompt += `GIT: On branch "${git.branch}"`;
|
||||||
|
if (git.remoteUrl) prompt += ` (remote: ${git.remoteUrl})`;
|
||||||
|
prompt += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const IDE_SESSION_BRIDGES = [
|
const IDE_SESSION_BRIDGES = [
|
||||||
'/home/gositeme/domains/gositeme.com/logs/alfred-ide/session.json',
|
'/home/gositeme/domains/gositeme.com/logs/alfred-ide/session.json',
|
||||||
'/home/gositeme/.alfred-ide/session.json'
|
'/home/gositeme/.alfred-ide/session.json'
|
||||||
@ -292,6 +624,20 @@ class AlfredCommanderProvider {
|
|||||||
palette: 'workbench.action.showCommands',
|
palette: 'workbench.action.showCommands',
|
||||||
split: 'workbench.action.splitEditor',
|
split: 'workbench.action.splitEditor',
|
||||||
newFile: 'workbench.action.files.newUntitledFile',
|
newFile: 'workbench.action.files.newUntitledFile',
|
||||||
|
git: 'workbench.view.scm',
|
||||||
|
problems: 'workbench.actions.view.problems',
|
||||||
|
search: 'workbench.action.findInFiles',
|
||||||
|
format: 'editor.action.formatDocument',
|
||||||
|
explorer: 'workbench.view.explorer',
|
||||||
|
debug: 'workbench.action.debug.start',
|
||||||
|
toggleComment: 'editor.action.commentLine',
|
||||||
|
goToDefinition: 'editor.action.revealDefinition',
|
||||||
|
findReferences: 'editor.action.goToReferences',
|
||||||
|
rename: 'editor.action.rename',
|
||||||
|
quickFix: 'editor.action.quickFix',
|
||||||
|
fold: 'editor.foldAll',
|
||||||
|
unfold: 'editor.unfoldAll',
|
||||||
|
closeAll: 'workbench.action.closeAllEditors',
|
||||||
};
|
};
|
||||||
const cmd = map[msg.cmd];
|
const cmd = map[msg.cmd];
|
||||||
if (cmd) vscode.commands.executeCommand(cmd);
|
if (cmd) vscode.commands.executeCommand(cmd);
|
||||||
@ -320,6 +666,95 @@ class AlfredCommanderProvider {
|
|||||||
this.context.globalState.update('alfredCommanderUserProfile', msg.profile);
|
this.context.globalState.update('alfredCommanderUserProfile', msg.profile);
|
||||||
userProfile = msg.profile;
|
userProfile = msg.profile;
|
||||||
}
|
}
|
||||||
|
} else if (msg.type === 'read-file') {
|
||||||
|
// Read file content and return to webview
|
||||||
|
const content = readFileContent(msg.filePath, msg.maxChars || 50000);
|
||||||
|
webviewView.webview.postMessage({ type: 'file-content', filePath: msg.filePath, content: content || '[File not found or unreadable]', id: msg.id });
|
||||||
|
} else if (msg.type === 'search-files') {
|
||||||
|
// Search for files by name pattern
|
||||||
|
const files = searchFilesInWorkspace(msg.pattern || '');
|
||||||
|
webviewView.webview.postMessage({ type: 'search-results', pattern: msg.pattern, files, id: msg.id });
|
||||||
|
} else if (msg.type === 'grep-search') {
|
||||||
|
// Grep search in workspace
|
||||||
|
const results = grepInWorkspace(msg.text || '', msg.filePattern || '');
|
||||||
|
webviewView.webview.postMessage({ type: 'grep-results', text: msg.text, results, id: msg.id });
|
||||||
|
} else if (msg.type === 'get-git-info') {
|
||||||
|
const git = getGitInfo();
|
||||||
|
webviewView.webview.postMessage({ type: 'git-info', info: git, id: msg.id });
|
||||||
|
} else if (msg.type === 'get-diagnostics') {
|
||||||
|
const diags = getDiagnosticsSummary();
|
||||||
|
webviewView.webview.postMessage({ type: 'diagnostics-info', info: diags, id: msg.id });
|
||||||
|
} else if (msg.type === 'get-project-structure') {
|
||||||
|
const structure = getProjectStructure();
|
||||||
|
webviewView.webview.postMessage({ type: 'project-structure', structure, id: msg.id });
|
||||||
|
} else if (msg.type === 'open-file') {
|
||||||
|
// Open a file in the editor
|
||||||
|
try {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const fullPath = path.isAbsolute(msg.filePath) ? msg.filePath : path.join(root, msg.filePath);
|
||||||
|
const uri = vscode.Uri.file(fullPath);
|
||||||
|
const doc = await vscode.workspace.openTextDocument(uri);
|
||||||
|
const editor = await vscode.window.showTextDocument(doc, { preview: false });
|
||||||
|
if (msg.line && msg.line > 0) {
|
||||||
|
const pos = new vscode.Position(msg.line - 1, 0);
|
||||||
|
editor.selection = new vscode.Selection(pos, pos);
|
||||||
|
editor.revealRange(new vscode.Range(pos, pos), vscode.TextEditorRevealType.InCenter);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Failed to open file: ' + e.message });
|
||||||
|
}
|
||||||
|
} else if (msg.type === 'apply-edit') {
|
||||||
|
// Apply a code edit to a specific file — the real power move
|
||||||
|
try {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const fullPath = path.isAbsolute(msg.filePath) ? msg.filePath : path.join(root, msg.filePath);
|
||||||
|
const uri = vscode.Uri.file(fullPath);
|
||||||
|
const doc = await vscode.workspace.openTextDocument(uri);
|
||||||
|
const editor = await vscode.window.showTextDocument(doc, { preview: false });
|
||||||
|
if (msg.oldText && msg.newText !== undefined) {
|
||||||
|
// Find and replace
|
||||||
|
const fullText = doc.getText();
|
||||||
|
const idx = fullText.indexOf(msg.oldText);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const startPos = doc.positionAt(idx);
|
||||||
|
const endPos = doc.positionAt(idx + msg.oldText.length);
|
||||||
|
await editor.edit(editBuilder => {
|
||||||
|
editBuilder.replace(new vscode.Range(startPos, endPos), msg.newText);
|
||||||
|
});
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Edit applied to ' + msg.filePath });
|
||||||
|
} else {
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Could not find the text to replace in ' + msg.filePath });
|
||||||
|
}
|
||||||
|
} else if (msg.line && msg.text !== undefined) {
|
||||||
|
// Insert at line
|
||||||
|
const pos = new vscode.Position(Math.max(0, msg.line - 1), 0);
|
||||||
|
await editor.edit(editBuilder => {
|
||||||
|
editBuilder.insert(pos, msg.text + '\n');
|
||||||
|
});
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Inserted at line ' + msg.line + ' in ' + msg.filePath });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Edit failed: ' + e.message });
|
||||||
|
}
|
||||||
|
} else if (msg.type === 'create-file') {
|
||||||
|
// Create a new file with content
|
||||||
|
try {
|
||||||
|
const root = getWorkspaceRoot();
|
||||||
|
const fullPath = path.isAbsolute(msg.filePath) ? msg.filePath : path.join(root, msg.filePath);
|
||||||
|
const dir = path.dirname(fullPath);
|
||||||
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||||
|
fs.writeFileSync(fullPath, msg.content || '', 'utf8');
|
||||||
|
const uri = vscode.Uri.file(fullPath);
|
||||||
|
const doc = await vscode.workspace.openTextDocument(uri);
|
||||||
|
await vscode.window.showTextDocument(doc, { preview: false });
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Created: ' + msg.filePath });
|
||||||
|
} catch (e) {
|
||||||
|
webviewView.webview.postMessage({ type: 'command-result', text: 'Create failed: ' + e.message });
|
||||||
|
}
|
||||||
|
} else if (msg.type === 'get-context') {
|
||||||
|
// Return full workspace context to webview
|
||||||
|
const ctx = buildFullContext();
|
||||||
|
webviewView.webview.postMessage({ type: 'workspace-context', context: ctx, id: msg.id });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -328,6 +763,23 @@ class AlfredCommanderProvider {
|
|||||||
webviewView.webview.postMessage({ type: 'user-profile', profile: userProfile });
|
webviewView.webview.postMessage({ type: 'user-profile', profile: userProfile });
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Push live editor context updates to webview
|
||||||
|
vscode.window.onDidChangeActiveTextEditor(() => {
|
||||||
|
if (this.view) {
|
||||||
|
const fileInfo = getActiveFileDetails();
|
||||||
|
if (fileInfo) {
|
||||||
|
this.view.webview.postMessage({ type: 'active-file-changed', file: fileInfo });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
vscode.workspace.onDidSaveTextDocument((doc) => {
|
||||||
|
if (this.view) {
|
||||||
|
const rel = vscode.workspace.asRelativePath(doc.uri, false);
|
||||||
|
this.view.webview.postMessage({ type: 'file-saved', file: rel, language: doc.languageId });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,27 +877,17 @@ function generateTTS(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getEditorContext() {
|
function getEditorContext() {
|
||||||
const editor = vscode.window.activeTextEditor;
|
return buildFullContext();
|
||||||
if (!editor) return '';
|
|
||||||
const doc = editor.document;
|
|
||||||
const sel = editor.selection;
|
|
||||||
let ctx = `[IDE Context] File: ${doc.fileName}, Language: ${doc.languageId}, Lines: ${doc.lineCount}`;
|
|
||||||
if (!sel.isEmpty) {
|
|
||||||
const selected = doc.getText(sel).substring(0, 500);
|
|
||||||
ctx += `, Selected code:\n\`\`\`${doc.languageId}\n${selected}\n\`\`\``;
|
|
||||||
} else {
|
|
||||||
const line = doc.lineAt(sel.active.line);
|
|
||||||
ctx += `, Current line ${sel.active.line + 1}: ${line.text.substring(0, 200)}`;
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryAlfredAPI(prompt, agent, editorContext, selectedModel = 'sonnet', images = [], pdfFiles = [], attachmentTexts = [], zipFiles = [], multiplier = 30) {
|
async function queryAlfredAPI(prompt, agent, editorContext, selectedModel = 'sonnet', images = [], pdfFiles = [], attachmentTexts = [], zipFiles = [], multiplier = 30) {
|
||||||
|
const systemPrompt = buildSystemPrompt(userProfile);
|
||||||
const payload = {
|
const payload = {
|
||||||
message: prompt, agent: agent || 'alfred',
|
message: prompt, agent: agent || 'alfred',
|
||||||
context: editorContext || '', channel: 'ide-chat',
|
context: editorContext || '', channel: 'ide-chat',
|
||||||
conv_id: convId || '', model: selectedModel,
|
conv_id: convId || '', model: selectedModel,
|
||||||
token_multiplier: multiplier || 30
|
token_multiplier: multiplier || 30,
|
||||||
|
system_prompt: systemPrompt
|
||||||
};
|
};
|
||||||
const ideToken = readLocalIdeToken();
|
const ideToken = readLocalIdeToken();
|
||||||
const ideIdentity = buildIdeIdentityPayload();
|
const ideIdentity = buildIdeIdentityPayload();
|
||||||
@ -869,6 +1311,16 @@ function getWebviewContent(injectedToken, injectedIdentity) {
|
|||||||
.ide-quick-label { font-size: 9px; color: var(--vscode-descriptionForeground); text-transform: uppercase; letter-spacing: 0.5px; margin-right: 4px; flex-shrink: 0; }
|
.ide-quick-label { font-size: 9px; color: var(--vscode-descriptionForeground); text-transform: uppercase; letter-spacing: 0.5px; margin-right: 4px; flex-shrink: 0; }
|
||||||
.ide-q-btn { font-size: 10px; padding: 5px 10px; border-radius: 6px; border: 1px solid var(--vscode-input-border, #30363d); background: var(--vscode-input-background, #161b22); color: var(--vscode-foreground); cursor: pointer; white-space: nowrap; }
|
.ide-q-btn { font-size: 10px; padding: 5px 10px; border-radius: 6px; border: 1px solid var(--vscode-input-border, #30363d); background: var(--vscode-input-background, #161b22); color: var(--vscode-foreground); cursor: pointer; white-space: nowrap; }
|
||||||
.ide-q-btn:hover { border-color: #e2b340; color: #e2b340; }
|
.ide-q-btn:hover { border-color: #e2b340; color: #e2b340; }
|
||||||
|
.code-block-wrap { margin: 6px 0; border: 1px solid var(--vscode-panel-border, #30363d); border-radius: 6px; overflow: hidden; }
|
||||||
|
.code-block-header { display: flex; align-items: center; gap: 4px; padding: 4px 8px; background: rgba(255,255,255,0.03); border-bottom: 1px solid var(--vscode-panel-border, #30363d); }
|
||||||
|
.code-lang { font-size: 9px; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; flex: 1; }
|
||||||
|
.cb-btn { font-size: 9px; padding: 2px 8px; border-radius: 4px; border: 1px solid var(--vscode-input-border, #30363d); background: transparent; cursor: pointer; font-family: inherit; }
|
||||||
|
.cb-copy { color: #60a5fa; }
|
||||||
|
.cb-copy:hover { background: rgba(96,165,250,0.15); border-color: #60a5fa; }
|
||||||
|
.cb-insert { color: #22c55e; }
|
||||||
|
.cb-insert:hover { background: rgba(34,197,94,0.15); border-color: #22c55e; }
|
||||||
|
.cb-run { color: #e2b340; }
|
||||||
|
.cb-run:hover { background: rgba(226,179,64,0.15); border-color: #e2b340; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -950,11 +1402,15 @@ function getWebviewContent(injectedToken, injectedIdentity) {
|
|||||||
<button type="button" class="ide-q-btn" id="acIdePalette" title="Command palette">Commands</button>
|
<button type="button" class="ide-q-btn" id="acIdePalette" title="Command palette">Commands</button>
|
||||||
<button type="button" class="ide-q-btn" id="acIdeSplit" title="Split editor">Split</button>
|
<button type="button" class="ide-q-btn" id="acIdeSplit" title="Split editor">Split</button>
|
||||||
<button type="button" class="ide-q-btn" id="acIdeNew" title="New untitled file">New file</button>
|
<button type="button" class="ide-q-btn" id="acIdeNew" title="New untitled file">New file</button>
|
||||||
|
<button type="button" class="ide-q-btn" id="acIdeGit" title="Open source control">Git</button>
|
||||||
|
<button type="button" class="ide-q-btn" id="acIdeProblems" title="Show problems panel">Problems</button>
|
||||||
|
<button type="button" class="ide-q-btn" id="acIdeSearch" title="Search across files">Search</button>
|
||||||
|
<button type="button" class="ide-q-btn" id="acIdeFormat" title="Format document">Format</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-area" id="chatArea">
|
<div class="chat-area" id="chatArea">
|
||||||
<div class="message alfred">
|
<div class="message alfred" id="greetingMsg">
|
||||||
<div class="sender">Alfred</div>
|
<div class="sender">Alfred</div>
|
||||||
<div>Ready. Claude Sonnet 4 connected. Speak or type.</div>
|
<div>Initializing workspace intelligence...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="attach-panel empty" id="attachPanel"></div>
|
<div class="attach-panel empty" id="attachPanel"></div>
|
||||||
@ -1616,8 +2072,21 @@ function scheduleHandsFreeRecord() {
|
|||||||
// ── Message formatting ─────────────────────────────────────────────────────
|
// ── Message formatting ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
function formatResponse(text) {
|
function formatResponse(text) {
|
||||||
|
let blockId = 0;
|
||||||
return text
|
return text
|
||||||
.replace(/\`\`\`(\\w*)\\n([\\s\\S]*?)\`\`\`/g, '<pre><code>$2</code></pre>')
|
.replace(/\`\`\`(\\w*)\\n([\\s\\S]*?)\`\`\`/g, function(match, lang, code) {
|
||||||
|
blockId++;
|
||||||
|
const escapedCode = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
const safeLang = (lang || 'text').replace(/[^a-zA-Z0-9]/g, '');
|
||||||
|
return '<div class="code-block-wrap" data-block-id="cb' + blockId + '">' +
|
||||||
|
'<div class="code-block-header"><span class="code-lang">' + safeLang + '</span>' +
|
||||||
|
'<button class="cb-btn cb-copy" onclick="copyCodeBlock(this)" title="Copy to clipboard">Copy</button>' +
|
||||||
|
'<button class="cb-btn cb-insert" onclick="insertCodeBlock(this)" title="Insert at cursor">Insert</button>' +
|
||||||
|
((['sh','bash','zsh','shell','cmd','powershell','terminal'].includes(safeLang)) ?
|
||||||
|
'<button class="cb-btn cb-run" onclick="runCodeBlock(this)" title="Run in terminal">Run</button>' : '') +
|
||||||
|
'</div>' +
|
||||||
|
'<pre><code data-code="' + btoa(unescape(encodeURIComponent(code))) + '">' + escapedCode + '</code></pre></div>';
|
||||||
|
})
|
||||||
.replace(/\`([^\`]+)\`/g, '<code>$1</code>')
|
.replace(/\`([^\`]+)\`/g, '<code>$1</code>')
|
||||||
.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')
|
.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')
|
||||||
.replace(/\\n/g, '<br>');
|
.replace(/\\n/g, '<br>');
|
||||||
@ -1724,10 +2193,13 @@ function processInput(text) {
|
|||||||
// Direct browser-to-API chat (primary path — no extension host IPC needed)
|
// Direct browser-to-API chat (primary path — no extension host IPC needed)
|
||||||
async function alfredDirectChat(reqId, text, agent, model, images, pdfs, texts, zips, multiplier) {
|
async function alfredDirectChat(reqId, text, agent, model, images, pdfs, texts, zips, multiplier) {
|
||||||
try {
|
try {
|
||||||
|
// Request workspace context from extension host for richer payload
|
||||||
|
vscode.postMessage({ type: 'get-context', id: reqId });
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
message: text, agent: agent || 'alfred', model: model || 'sonnet',
|
message: text, agent: agent || 'alfred', model: model || 'sonnet',
|
||||||
token_multiplier: multiplier || 30,
|
token_multiplier: multiplier || 30,
|
||||||
channel: 'ide-chat', context: '', conv_id: ''
|
channel: 'ide-chat', context: window.__lastWorkspaceContext || '', conv_id: ''
|
||||||
};
|
};
|
||||||
// Include auth token injected from extension host
|
// Include auth token injected from extension host
|
||||||
if (window.__alfredToken) payload.ide_session_token = window.__alfredToken;
|
if (window.__alfredToken) payload.ide_session_token = window.__alfredToken;
|
||||||
@ -1864,6 +2336,54 @@ window.addEventListener('message', (event) => {
|
|||||||
else if (msg.type === 'user-profile') {
|
else if (msg.type === 'user-profile') {
|
||||||
applyIdentity(msg.profile || {});
|
applyIdentity(msg.profile || {});
|
||||||
}
|
}
|
||||||
|
else if (msg.type === 'workspace-context') {
|
||||||
|
// Cache latest workspace context for direct API calls
|
||||||
|
window.__lastWorkspaceContext = msg.context || '';
|
||||||
|
}
|
||||||
|
else if (msg.type === 'active-file-changed') {
|
||||||
|
// Update status with current file info
|
||||||
|
if (msg.file && msg.file.file) {
|
||||||
|
const statusHint = msg.file.file + ' (' + msg.file.language + ', L' + msg.file.cursorLine + ')';
|
||||||
|
logTelemetry('active-file: ' + statusHint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg.type === 'file-saved') {
|
||||||
|
logTelemetry('saved: ' + (msg.file || 'unknown'));
|
||||||
|
}
|
||||||
|
else if (msg.type === 'file-content') {
|
||||||
|
// File content received — add as system message
|
||||||
|
if (msg.content) {
|
||||||
|
addMessage('system', 'File: ' + (msg.filePath || '') + '\\n' + msg.content.substring(0, 2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg.type === 'search-results') {
|
||||||
|
if (msg.files && msg.files.length > 0) {
|
||||||
|
addMessage('system', 'Found ' + msg.files.length + ' files matching "' + (msg.pattern || '') + '":\\n' + msg.files.join('\\n'));
|
||||||
|
} else {
|
||||||
|
addMessage('system', 'No files found matching "' + (msg.pattern || '') + '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg.type === 'grep-results') {
|
||||||
|
if (msg.results) {
|
||||||
|
addMessage('system', 'Grep results for "' + (msg.text || '') + '":\\n' + msg.results.substring(0, 3000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg.type === 'git-info') {
|
||||||
|
if (msg.info) {
|
||||||
|
const g = msg.info;
|
||||||
|
addMessage('system', 'Git: branch=' + g.branch + ', last commit: ' + g.lastCommit + (g.dirty ? ', changes: ' + g.dirty : '') + (g.remoteUrl ? ', remote: ' + g.remoteUrl : ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg.type === 'diagnostics-info') {
|
||||||
|
if (msg.info) {
|
||||||
|
addMessage('system', 'Diagnostics: ' + msg.info.errors + ' errors, ' + msg.info.warnings + ' warnings' + (msg.info.errorFiles.length ? ' in: ' + msg.info.errorFiles.join(', ') : ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg.type === 'project-structure') {
|
||||||
|
if (msg.structure) {
|
||||||
|
addMessage('system', 'Project structure:\\n' + msg.structure.substring(0, 3000));
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Event listeners ────────────────────────────────────────────────────────
|
// ── Event listeners ────────────────────────────────────────────────────────
|
||||||
@ -1926,7 +2446,7 @@ if (document.readyState === 'loading') {
|
|||||||
document.addEventListener('DOMContentLoaded', function() { bindSend(); });
|
document.addEventListener('DOMContentLoaded', function() { bindSend(); });
|
||||||
}
|
}
|
||||||
(function bindIdeQuick() {
|
(function bindIdeQuick() {
|
||||||
[['acIdeTerminal','terminal'], ['acIdeSave','save'], ['acIdeSaveAll','saveAll'], ['acIdePalette','palette'], ['acIdeSplit','split'], ['acIdeNew','newFile']].forEach(([id, cmd]) => {
|
[['acIdeTerminal','terminal'], ['acIdeSave','save'], ['acIdeSaveAll','saveAll'], ['acIdePalette','palette'], ['acIdeSplit','split'], ['acIdeNew','newFile'], ['acIdeGit','git'], ['acIdeProblems','problems'], ['acIdeSearch','search'], ['acIdeFormat','format']].forEach(([id, cmd]) => {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) el.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); vscode.postMessage({ type: 'ide-quick', cmd }); });
|
if (el) el.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); vscode.postMessage({ type: 'ide-quick', cmd }); });
|
||||||
});
|
});
|
||||||
@ -1956,6 +2476,79 @@ window.addEventListener('unhandledrejection', (ev) => {
|
|||||||
logTelemetry('promise-error: ' + msg);
|
logTelemetry('promise-error: ' + msg);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Code block action buttons ────────────────────────────────────────────
|
||||||
|
|
||||||
|
function copyCodeBlock(btn) {
|
||||||
|
try {
|
||||||
|
const wrap = btn.closest('.code-block-wrap');
|
||||||
|
const codeEl = wrap.querySelector('code[data-code]');
|
||||||
|
const code = decodeURIComponent(escape(atob(codeEl.getAttribute('data-code'))));
|
||||||
|
navigator.clipboard.writeText(code).then(() => {
|
||||||
|
btn.textContent = 'Copied!';
|
||||||
|
setTimeout(() => { btn.textContent = 'Copy'; }, 1500);
|
||||||
|
}).catch(() => {
|
||||||
|
// Fallback for no clipboard API
|
||||||
|
const ta = document.createElement('textarea');
|
||||||
|
ta.value = code;
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
btn.textContent = 'Copied!';
|
||||||
|
setTimeout(() => { btn.textContent = 'Copy'; }, 1500);
|
||||||
|
});
|
||||||
|
} catch(e) { setStatus('Copy failed: ' + e.message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertCodeBlock(btn) {
|
||||||
|
try {
|
||||||
|
const wrap = btn.closest('.code-block-wrap');
|
||||||
|
const codeEl = wrap.querySelector('code[data-code]');
|
||||||
|
const code = decodeURIComponent(escape(atob(codeEl.getAttribute('data-code'))));
|
||||||
|
vscode.postMessage({ type: 'insert-code', code: code });
|
||||||
|
btn.textContent = 'Inserted!';
|
||||||
|
setTimeout(() => { btn.textContent = 'Insert'; }, 1500);
|
||||||
|
} catch(e) { setStatus('Insert failed: ' + e.message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCodeBlock(btn) {
|
||||||
|
try {
|
||||||
|
const wrap = btn.closest('.code-block-wrap');
|
||||||
|
const codeEl = wrap.querySelector('code[data-code]');
|
||||||
|
const code = decodeURIComponent(escape(atob(codeEl.getAttribute('data-code'))));
|
||||||
|
vscode.postMessage({ type: 'run-terminal', command: code.trim() });
|
||||||
|
btn.textContent = 'Sent!';
|
||||||
|
setTimeout(() => { btn.textContent = 'Run'; }, 1500);
|
||||||
|
addMessage('system', 'Command sent to terminal.');
|
||||||
|
} catch(e) { setStatus('Run failed: ' + e.message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Commander identity greeting ──────────────────────────────────────────
|
||||||
|
|
||||||
|
(function commanderGreeting() {
|
||||||
|
const ident = window.__alfredIdentity || {};
|
||||||
|
const greetEl = document.getElementById('greetingMsg');
|
||||||
|
if (!greetEl) return;
|
||||||
|
const contentEl = greetEl.querySelector('div:not(.sender)');
|
||||||
|
if (!contentEl) return;
|
||||||
|
|
||||||
|
const isCommander = ident.ide_client_id === 33;
|
||||||
|
const name = ident.ide_name || 'there';
|
||||||
|
|
||||||
|
if (isCommander) {
|
||||||
|
contentEl.innerHTML = '<strong>Commander on deck.</strong> All systems nominal.<br>' +
|
||||||
|
'<span style="color:#e2b340;">Full workspace intelligence active. Voice, code actions, file ops, git — everything is live.</span><br>' +
|
||||||
|
'<span style="font-size:10px;color:#8b949e;">Sonnet 4.6 default · ' + new Date().toLocaleDateString('en-US', {weekday:'long', month:'long', day:'numeric'}) + ' · GoForge synced</span>';
|
||||||
|
} else if (name && name !== 'there') {
|
||||||
|
contentEl.innerHTML = 'Welcome, <strong>' + name + '</strong>. Alfred IDE is ready.<br>' +
|
||||||
|
'<span style="font-size:10px;color:#8b949e;">Type a question, paste code, attach files, or use voice. I have full context of your workspace.</span>';
|
||||||
|
} else {
|
||||||
|
contentEl.innerHTML = 'Alfred IDE ready. Full AI coding assistant at your service.<br>' +
|
||||||
|
'<span style="font-size:10px;color:#8b949e;">Speak or type. Sonnet 4.6 connected.</span>';
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
setStatus('Ready');
|
setStatus('Ready');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -5,8 +5,6 @@
|
|||||||
"unwantedRecommendations": [
|
"unwantedRecommendations": [
|
||||||
"ms-vscode-remote.remote-ssh",
|
"ms-vscode-remote.remote-ssh",
|
||||||
"ms-vscode.remote-server",
|
"ms-vscode.remote-server",
|
||||||
"ms-vscode.remote-explorer",
|
"ms-vscode.remote-explorer"
|
||||||
"github.copilot",
|
|
||||||
"github.copilot-chat"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,11 @@
|
|||||||
"description": "Fresh Alfred panel: all agents & models, voice STT/TTS, attachments, hands-free. IDE shortcuts are explicit buttons — chat text always goes to AI.",
|
"description": "Fresh Alfred panel: all agents & models, voice STT/TTS, attachments, hands-free. IDE shortcuts are explicit buttons — chat text always goes to AI.",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"publisher": "gositeme",
|
"publisher": "gositeme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://alfredlinux.com/forge/commander/alfred-ide.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://alfredlinux.com/forge/commander/alfred-ide",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.70.0"
|
"vscode": "^1.70.0"
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user