@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
228 lines • 7.95 kB
JavaScript
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