UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

228 lines 7.95 kB
import { promises as fs } from 'fs'; import path from 'path'; import os from 'os'; import { StorageManager } from '../storage/storage-manager.js'; export class ConfigManager { config; configPath = null; storageManager; constructor() { this.storageManager = new StorageManager(); this.config = this.getDefaultConfig(); } getDefaultConfig() { return { projectMode: 'new', workspaceRoot: process.cwd(), dataLocation: '.atlas/', integration: { respectGitignore: true, preserveExisting: true, modules: { kanban: true, development: true, documentation: true, 'product-requirements': true, agile: true, }, }, features: { tddEnforcement: 'strict', autoInit: false, autoBackup: true, }, storage: { mode: 'local', isolation: 'project', }, webDashboard: { enabled: true, port: process.env.ATLAS_DASHBOARD_PORT ? parseInt(process.env.ATLAS_DASHBOARD_PORT) : process.env.NODE_ENV === 'test' ? 0 : 3001, host: 'localhost', autoOpen: false, features: { performance: true, security: true, agile: true, errors: true }, realTimeUpdates: true, exportEnabled: true }, version: '1.0.0', }; } async getConfigPath() { // Store config in the storage location, not in project root by default const location = await this.storageManager.getStorageLocation(); return path.join(location.config, 'atlas.config.json'); } async load() { // First try to load from project root try { const localConfigPath = path.join(process.cwd(), 'atlas.config.json'); const data = await fs.readFile(localConfigPath, 'utf-8'); this.config = { ...this.getDefaultConfig(), ...JSON.parse(data) }; return; } catch { // Not in project root, continue } // Then try from storage location try { if (!this.configPath) { this.configPath = await this.getConfigPath(); } const data = await fs.readFile(this.configPath, 'utf-8'); this.config = { ...this.getDefaultConfig(), ...JSON.parse(data) }; } catch (error) { // Use default config if file doesn't exist this.config = this.getDefaultConfig(); } } async save() { // Save to current directory for tests/local use const localConfigPath = path.join(process.cwd(), 'atlas.config.json'); await fs.writeFile(localConfigPath, JSON.stringify(this.config, null, 2)); } async detectProject() { const result = { hasGit: false, hasPackageJson: false, hasTsConfig: false, hasTests: false, suggestedMode: 'new', existingTools: [], }; // Check for Git try { await fs.access(path.join(process.cwd(), '.git')); result.hasGit = true; result.existingTools.push('git'); } catch { } // Check for Node.js project try { await fs.access(path.join(process.cwd(), 'package.json')); result.hasPackageJson = true; result.projectType = 'node'; const packageJson = JSON.parse(await fs.readFile(path.join(process.cwd(), 'package.json'), 'utf-8')); // Detect test framework const deps = { ...packageJson.dependencies, ...packageJson.devDependencies }; if (deps.jest || deps['@jest/core']) { result.testFramework = 'jest'; result.hasTests = true; } else if (deps.mocha) { result.testFramework = 'mocha'; result.hasTests = true; } else if (deps.vitest) { result.testFramework = 'vitest'; result.hasTests = true; } // Check for existing PM tools if (deps['@atlassian/jira']) result.existingTools.push('jira'); if (deps.trello) result.existingTools.push('trello'); } catch { } // Check for TypeScript try { await fs.access(path.join(process.cwd(), 'tsconfig.json')); result.hasTsConfig = true; result.existingTools.push('typescript'); } catch { } // Determine suggested mode if (result.hasGit && result.hasPackageJson) { result.suggestedMode = 'existing'; } // Check for monorepo indicators try { const files = await fs.readdir(process.cwd()); const hasMultiplePackageJsons = 0; for (const file of files) { const filePath = path.join(process.cwd(), file); const stat = await fs.stat(filePath); if (stat.isDirectory() && !file.startsWith('.')) { try { await fs.access(path.join(filePath, 'package.json')); result.suggestedMode = 'multi-repo'; break; } catch { } } } // Check for lerna, yarn workspaces, etc. if (files.includes('lerna.json') || files.includes('pnpm-workspace.yaml')) { result.suggestedMode = 'multi-repo'; } } catch { } return result; } async getStoragePath() { const location = await this.storageManager.getStorageLocation(); return location.data; } // Backwards compatibility - sync version getDataPath() { // Return based on current config if (this.config.storage?.mode === 'home' && this.config.projectName) { return path.join(os.homedir(), '.atlas', 'projects', this.config.projectName); } if (this.config.storage?.mode === 'custom' && this.config.storage?.path) { return this.config.storage.path; } return path.join(this.config.workspaceRoot || process.cwd(), '.atlas'); } getStorageManager() { return this.storageManager; } get() { return { ...this.config }; } set(config) { this.config = { ...this.config, ...config }; } isModuleEnabled(module) { const modules = this.config.integration?.modules; return modules?.[module] ?? true; } getProjectMode() { return this.config.projectMode; } getProjectId() { return this.config.projectId; } isMultiRepo() { return this.config.projectMode === 'multi-repo'; } getRepositories() { return this.config.repositories || []; } isDashboardEnabled() { return this.config.webDashboard?.enabled ?? true; } getDashboardConfig() { const defaults = this.getDefaultConfig().webDashboard; return { ...defaults, ...this.config.webDashboard }; } updateDashboardConfig(dashboardConfig) { this.config.webDashboard = { ...this.config.webDashboard, ...dashboardConfig }; } logModuleLoad(moduleName, type, toolCount) { console.log(`✅ Loaded ${moduleName} module (${type}) with ${toolCount} tools`); } } //# sourceMappingURL=config-manager.js.map