@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
541 lines • 20.9 kB
JavaScript
import { randomUUID } from 'crypto';
import { promises as fs } from 'fs';
import path from 'path';
import { createTool, createSuccessResult, createErrorResult } from '../../core/tool-framework.js';
/**
* Check if a directory is a git repository
*/
async function isGitRepository(dirPath) {
try {
await fs.access(path.join(dirPath, '.git'));
return true;
}
catch {
return false;
}
}
/**
* Auto-detect repositories in a directory
*/
async function detectRepositories(rootPath) {
const repositories = [];
try {
const entries = await fs.readdir(rootPath, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && !entry.name.startsWith('.')) {
const fullPath = path.join(rootPath, entry.name);
// Check for version control systems
let repoType = 'local';
if (await isGitRepository(fullPath)) {
repoType = 'git';
}
else {
// Check for other VCS
try {
await fs.access(path.join(fullPath, '.svn'));
repoType = 'svn';
}
catch {
try {
await fs.access(path.join(fullPath, '.hg'));
repoType = 'mercurial';
}
catch {
// Check if it's a Node.js package
try {
await fs.access(path.join(fullPath, 'package.json'));
repoType = 'local';
}
catch {
// Skip non-repository directories
continue;
}
}
}
}
repositories.push({
name: entry.name,
path: fullPath,
type: repoType
});
}
}
}
catch (error) {
console.error('Error detecting repositories:', error);
}
return repositories;
}
/**
* Create a new workspace
*/
const createWorkspaceTool = createTool({
name: 'create_workspace',
description: 'Create a new multi-repository workspace',
category: 'workspace',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Workspace name',
minLength: 1,
maxLength: 200
},
description: {
type: 'string',
description: 'Workspace description',
maxLength: 1000
},
rootPath: {
type: 'string',
description: 'Root directory path (defaults to current directory)'
},
autoDetect: {
type: 'boolean',
description: 'Automatically detect repositories',
default: true
},
primaryRepoPath: {
type: 'string',
description: 'Path to the primary repository'
}
},
required: ['name'],
additionalProperties: false
},
async execute(input, context) {
try {
const rootPath = input.rootPath || process.cwd();
// Verify root path exists
try {
await fs.access(rootPath);
}
catch {
return createErrorResult({
code: 'INVALID_PATH',
message: 'Root path does not exist',
details: { rootPath },
category: 'validation'
});
}
// Resolve absolute path
const absoluteRootPath = path.resolve(rootPath);
// Check for duplicate workspace names
const existingWorkspace = await context.db.get('SELECT id FROM workspaces WHERE name = ?', [input.name]);
if (existingWorkspace.success && existingWorkspace.data) {
return createErrorResult({
code: 'DUPLICATE_RESOURCE',
message: 'A workspace with this name already exists',
category: 'validation'
});
}
const workspaceId = `ws-${randomUUID()}`;
const now = Date.now();
// Create workspace
const result = await context.db.run(`INSERT INTO workspaces
(id, name, description, root_path, active, settings, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
workspaceId,
input.name,
input.description || '',
absoluteRootPath,
false, // Not active by default
JSON.stringify({}),
now,
now
]);
if (!result.success) {
return createErrorResult({
code: 'DATABASE_ERROR',
message: 'Failed to create workspace',
details: { error: result.error },
category: 'system'
});
}
// Auto-detect repositories if requested
const detectedRepos = [];
if (input.autoDetect !== false) {
const repos = await detectRepositories(absoluteRootPath);
for (const repo of repos) {
const repoId = `repo-${randomUUID()}`;
const isPrimary = repo.path === input.primaryRepoPath ||
(repos.length === 1 && !input.primaryRepoPath);
await context.db.run(`INSERT INTO workspace_repositories
(id, workspace_id, name, path, type, is_primary, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
repoId,
workspaceId,
repo.name,
repo.path,
repo.type,
isPrimary,
now,
now
]);
detectedRepos.push({
id: repoId,
name: repo.name,
path: repo.path,
type: repo.type,
primary: isPrimary
});
}
}
// If no active workspace exists, make this one active
const activeWorkspaceCheck = await context.db.get('SELECT id FROM workspaces WHERE active = TRUE');
if (!activeWorkspaceCheck.success || !activeWorkspaceCheck.data) {
await context.db.run('UPDATE workspaces SET active = TRUE WHERE id = ?', [workspaceId]);
}
return createSuccessResult({
workspace: {
id: workspaceId,
name: input.name,
description: input.description || '',
rootPath: absoluteRootPath,
active: !activeWorkspaceCheck.data,
repositories: detectedRepos,
createdAt: new Date(now).toISOString()
},
message: `Workspace "${input.name}" created successfully`,
detectedRepositories: detectedRepos.length,
nextSteps: [
detectedRepos.length === 0 ? 'Add repositories to your workspace' : null,
!detectedRepos.some(r => r.primary) ? 'Designate a primary repository' : null,
'Switch to this workspace to make it active'
].filter(Boolean)
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to create workspace: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Add a repository to a workspace
*/
const addRepositoryTool = createTool({
name: 'add_repository',
description: 'Add a repository to an existing workspace',
category: 'workspace',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Workspace ID',
pattern: '^ws-[a-f0-9-]+$'
},
name: {
type: 'string',
description: 'Repository name (defaults to directory name)',
maxLength: 200
},
path: {
type: 'string',
description: 'Repository path'
},
type: {
type: 'string',
enum: ['git', 'svn', 'mercurial', 'local'],
default: 'local',
description: 'Repository type'
},
setPrimary: {
type: 'boolean',
description: 'Set as primary repository',
default: false
},
remote: {
type: 'string',
description: 'Remote repository URL'
},
branch: {
type: 'string',
description: 'Current branch name'
}
},
required: ['workspaceId', 'path'],
additionalProperties: false
},
async execute(input, context) {
try {
// Verify workspace exists
const workspaceResult = await context.db.get('SELECT id, name FROM workspaces WHERE id = ?', [input.workspaceId]);
if (!workspaceResult.success || !workspaceResult.data) {
return createErrorResult({
code: 'RESOURCE_NOT_FOUND',
message: 'Workspace not found',
category: 'validation'
});
}
const workspace = workspaceResult.data;
// Verify repository path exists
try {
await fs.access(input.path);
}
catch {
return createErrorResult({
code: 'INVALID_PATH',
message: 'Repository path does not exist',
details: { path: input.path },
category: 'validation'
});
}
const absolutePath = path.resolve(input.path);
const repoName = input.name || path.basename(absolutePath);
// Check if repository already exists in workspace
const existingRepo = await context.db.get('SELECT id FROM workspace_repositories WHERE workspace_id = ? AND path = ?', [input.workspaceId, absolutePath]);
if (existingRepo.success && existingRepo.data) {
return createErrorResult({
code: 'DUPLICATE_RESOURCE',
message: 'Repository already exists in this workspace',
category: 'validation'
});
}
// Auto-detect repository type if not specified
let repoType = input.type || 'local';
if (repoType === 'local') {
if (await isGitRepository(absolutePath)) {
repoType = 'git';
}
}
const repoId = `repo-${randomUUID()}`;
const now = Date.now();
// If setting as primary, unset other primary repos
if (input.setPrimary) {
await context.db.run('UPDATE workspace_repositories SET is_primary = FALSE WHERE workspace_id = ?', [input.workspaceId]);
}
// Add repository
const result = await context.db.run(`INSERT INTO workspace_repositories
(id, workspace_id, name, path, type, is_primary, remote_url, current_branch,
created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
repoId,
input.workspaceId,
repoName,
absolutePath,
repoType,
input.setPrimary || false,
input.remote || null,
input.branch || null,
now,
now
]);
if (!result.success) {
return createErrorResult({
code: 'DATABASE_ERROR',
message: 'Failed to add repository',
details: { error: result.error },
category: 'system'
});
}
// Update workspace modification time
await context.db.run('UPDATE workspaces SET updated_at = ? WHERE id = ?', [now, input.workspaceId]);
return createSuccessResult({
repository: {
id: repoId,
workspaceId: input.workspaceId,
name: repoName,
path: absolutePath,
type: repoType,
primary: input.setPrimary || false,
remote: input.remote || null,
branch: input.branch || null,
createdAt: new Date(now).toISOString()
},
workspace: {
id: workspace.id,
name: workspace.name
},
message: `Repository "${repoName}" added to workspace "${workspace.name}"`,
detectedType: repoType !== (input.type || 'local'),
nextSteps: input.setPrimary ? [] : ['Consider setting this as the primary repository']
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to add repository: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* List repositories in a workspace
*/
const listRepositoriesTool = createTool({
name: 'list_repositories',
description: 'List all repositories in a workspace',
category: 'workspace',
readOnly: true,
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Workspace ID',
pattern: '^ws-[a-f0-9-]+$'
}
},
required: ['workspaceId'],
additionalProperties: false
},
async execute(input, context) {
try {
// Get workspace details
const workspaceResult = await context.db.get('SELECT * FROM workspaces WHERE id = ?', [input.workspaceId]);
if (!workspaceResult.success || !workspaceResult.data) {
return createErrorResult({
code: 'RESOURCE_NOT_FOUND',
message: 'Workspace not found',
category: 'validation'
});
}
const workspace = workspaceResult.data;
// Get all repositories
const reposResult = await context.db.query('SELECT * FROM workspace_repositories WHERE workspace_id = ? ORDER BY is_primary DESC, name', [input.workspaceId]);
if (!reposResult.success) {
return createErrorResult({
code: 'DATABASE_ERROR',
message: 'Failed to list repositories',
details: { error: reposResult.error },
category: 'system'
});
}
const repositories = (reposResult.data || []).map((repo) => ({
id: repo.id,
name: repo.name,
path: repo.path,
type: repo.type,
primary: repo.is_primary,
remote: repo.remote_url,
branch: repo.current_branch,
lastSync: repo.last_sync ? new Date(repo.last_sync).toISOString() : null,
dependencies: JSON.parse(repo.dependencies || '[]')
}));
// Get repository type breakdown
const typeBreakdown = repositories.reduce((acc, repo) => {
acc[repo.type] = (acc[repo.type] || 0) + 1;
return acc;
}, {});
return createSuccessResult({
workspace: {
id: workspace.id,
name: workspace.name,
description: workspace.description,
rootPath: workspace.root_path,
active: workspace.active,
createdAt: new Date(workspace.created_at).toISOString(),
updatedAt: new Date(workspace.updated_at).toISOString()
},
repositories,
statistics: {
total: repositories.length,
byType: typeBreakdown,
primaryRepository: repositories.find((r) => r.primary)?.name || null
}
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to list repositories: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Switch active workspace
*/
const switchWorkspaceTool = createTool({
name: 'switch_workspace',
description: 'Switch the active workspace',
category: 'workspace',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Workspace ID to switch to',
pattern: '^ws-[a-f0-9-]+$'
}
},
required: ['workspaceId'],
additionalProperties: false
},
async execute(input, context) {
try {
// Verify workspace exists
const workspaceResult = await context.db.get('SELECT * FROM workspaces WHERE id = ?', [input.workspaceId]);
if (!workspaceResult.success || !workspaceResult.data) {
return createErrorResult({
code: 'RESOURCE_NOT_FOUND',
message: 'Workspace not found',
category: 'validation'
});
}
const workspace = workspaceResult.data;
// If already active, no-op
if (workspace.active) {
return createSuccessResult({
workspace: {
id: workspace.id,
name: workspace.name,
active: true
},
message: `Workspace "${workspace.name}" is already active`,
changed: false
});
}
// Deactivate all workspaces
await context.db.run('UPDATE workspaces SET active = FALSE');
// Activate target workspace
const now = Date.now();
await context.db.run('UPDATE workspaces SET active = TRUE, updated_at = ? WHERE id = ?', [now, input.workspaceId]);
// Get repository count
const repoCountResult = await context.db.get('SELECT COUNT(*) as count FROM workspace_repositories WHERE workspace_id = ?', [input.workspaceId]);
return createSuccessResult({
workspace: {
id: workspace.id,
name: workspace.name,
description: workspace.description,
rootPath: workspace.root_path,
active: true,
repositoryCount: repoCountResult.data?.count || 0
},
message: `Switched to workspace "${workspace.name}"`,
changed: true,
previousActiveWorkspace: null // Could track this if needed
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to switch workspace: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Setup workspace tools
*/
export async function setupWorkspaceTools() {
return {
module: 'workspace',
tools: [
createWorkspaceTool,
addRepositoryTool,
listRepositoriesTool,
switchWorkspaceTool
]
};
}
//# sourceMappingURL=tools.js.map