import { spawnSync } from 'node:child_process'; import { existsSync, mkdirSync, rmSync, symlinkSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; export interface UAInstallStatus { installed: boolean; path: string; dashboardPath: string; skillsPath: string; agentsSkillsPath: string; } const UA_PATH = join(homedir(), '.qwen-s', 'understand-anything'); const UA_PLUGIN_PATH = join(UA_PATH, 'understand-anything-plugin'); const UA_DASHBOARD_PATH = join(UA_PLUGIN_PATH, 'packages', 'dashboard'); const UA_SKILLS_PATH = join(UA_PLUGIN_PATH, 'skills'); const AGENTS_SKILLS_PATH = join(homedir(), '.agents', 'skills'); const PLUGIN_SYMLINK_PATH = join(homedir(), '.understand-anything-plugin'); const UA_REPO = 'https://github.com/Lum1104/Understand-Anything.git'; const GIT_TIMEOUT_MS = 5 * 60 * 1000; const NPM_TIMEOUT_MS = 3 * 60 * 1000; const UA_SKILLS = [ 'understand', 'understand-chat', 'understand-dashboard', 'understand-diff', 'understand-explain', 'understand-onboard', ] as const; export function getUAInstallStatus(): UAInstallStatus { return { installed: existsSync(UA_PLUGIN_PATH), path: UA_PATH, dashboardPath: UA_DASHBOARD_PATH, skillsPath: UA_SKILLS_PATH, agentsSkillsPath: AGENTS_SKILLS_PATH, }; } export async function installUA(): Promise { const gitCheck = spawnSync('git', ['--version'], { stdio: 'pipe' }); if (gitCheck.status !== 0) { throw new Error('git is required. Install git first.'); } mkdirSync(join(homedir(), '.qwen-s'), { recursive: true }); if (existsSync(UA_PATH)) { const pullResult = spawnSync('git', ['-C', UA_PATH, 'pull'], { stdio: 'inherit', timeout: GIT_TIMEOUT_MS, }); assertCommandSucceeded(pullResult.status, pullResult.error, 'Failed to update Understand-Anything repository'); } else { const cloneResult = spawnSync('git', ['clone', UA_REPO, UA_PATH], { stdio: 'inherit', timeout: GIT_TIMEOUT_MS, }); assertCommandSucceeded(cloneResult.status, cloneResult.error, 'Failed to clone Understand-Anything repository'); } const npmInstallResult = spawnSync('npm', ['install'], { cwd: UA_DASHBOARD_PATH, stdio: 'inherit', timeout: NPM_TIMEOUT_MS, }); if (npmInstallResult.status !== 0 || npmInstallResult.error !== undefined) { console.log('Dashboard install via workspace package failed, retrying from plugin root...'); const npmWorkspaceInstallResult = spawnSync('npm', ['install'], { cwd: UA_PLUGIN_PATH, stdio: 'inherit', timeout: NPM_TIMEOUT_MS, }); assertCommandSucceeded( npmWorkspaceInstallResult.status, npmWorkspaceInstallResult.error, 'Failed to install dashboard dependencies', ); } mkdirSync(AGENTS_SKILLS_PATH, { recursive: true }); for (const skill of UA_SKILLS) { const source = join(UA_SKILLS_PATH, skill); const target = join(AGENTS_SKILLS_PATH, skill); recreateSymlink(source, target); } recreateSymlink(UA_PLUGIN_PATH, PLUGIN_SYMLINK_PATH); console.log('✓ Understand-Anything installed successfully!'); console.log('Skills: ~/.agents/skills/understand (and 5 others)'); console.log(`Dashboard: ${UA_DASHBOARD_PATH}`); console.log(''); console.log('Next steps:'); console.log('1. Open Qwen Code in your project: cd /your/project && qwen'); console.log('2. Say: запусти скан с /understand'); console.log('3. After analysis: qwen-s scan arch --skip-ua'); } export async function ensureUAInstalled(): Promise { const status = getUAInstallStatus(); if (status.installed) { return; } console.log('Understand-Anything not found. Installing automatically...'); console.log('This requires git and internet access. Estimated time: 2-3 minutes.'); await installUA(); } function recreateSymlink(source: string, target: string): void { if (existsSync(target)) { rmSync(target, { recursive: true, force: true }); } symlinkSync(source, target); } function assertCommandSucceeded(status: number | null, error: Error | undefined, message: string): void { if (error !== undefined) { throw new Error(`${message}: ${error.message}`); } if (status !== 0) { throw new Error(`${message} (exit code ${String(status)})`); } }