@al76/tools-and-spec-workflow-mcp
Version:
MCP server for spec-driven development workflow with real-time web dashboard
97 lines • 3.6 kB
JavaScript
import { join, normalize, sep, resolve } from 'path';
import { access, stat, mkdir } from 'fs/promises';
import { constants } from 'fs';
export class PathUtils {
static getWorkflowRoot(projectPath) {
return normalize(join(projectPath, '.spec-workflow'));
}
static getSpecPath(projectPath, specName) {
return normalize(join(projectPath, '.spec-workflow', 'specs', specName));
}
static getArchiveSpecPath(projectPath, specName) {
return normalize(join(projectPath, '.spec-workflow', 'archive', 'specs', specName));
}
static getArchiveSpecsPath(projectPath) {
return normalize(join(projectPath, '.spec-workflow', 'archive', 'specs'));
}
static getSteeringPath(projectPath) {
return normalize(join(projectPath, '.spec-workflow', 'steering'));
}
static getTemplatesPath(projectPath) {
return normalize(join(projectPath, '.spec-workflow', 'templates'));
}
static getAgentsPath(projectPath) {
return normalize(join(projectPath, '.spec-workflow', 'agents'));
}
static getCommandsPath(projectPath) {
return normalize(join(projectPath, '.spec-workflow', 'commands'));
}
static getApprovalsPath(projectPath) {
return normalize(join(projectPath, '.spec-workflow', 'approvals'));
}
static getSpecApprovalPath(projectPath, specName) {
return normalize(join(projectPath, '.spec-workflow', 'approvals', specName));
}
// Ensure paths work across Windows, macOS, Linux
static toPlatformPath(path) {
return path.split('/').join(sep);
}
static toUnixPath(path) {
return path.split(sep).join('/');
}
// Get relative path from project root
static getRelativePath(projectPath, fullPath) {
const normalizedProject = normalize(projectPath);
const normalizedFull = normalize(fullPath);
if (normalizedFull.startsWith(normalizedProject)) {
return normalizedFull.slice(normalizedProject.length + 1);
}
return normalizedFull;
}
}
export async function validateProjectPath(projectPath) {
try {
// Resolve to absolute path
const absolutePath = resolve(projectPath);
// Check if path exists
await access(absolutePath, constants.F_OK);
// Ensure it's a directory
const stats = await stat(absolutePath);
if (!stats.isDirectory()) {
throw new Error(`Project path is not a directory: ${absolutePath}`);
}
return absolutePath;
}
catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`Project path does not exist: ${projectPath}`);
}
throw error;
}
}
export async function ensureDirectoryExists(dirPath) {
try {
await access(dirPath, constants.F_OK);
}
catch {
await mkdir(dirPath, { recursive: true });
}
}
export async function ensureWorkflowDirectory(projectPath) {
const workflowRoot = PathUtils.getWorkflowRoot(projectPath);
// Create all necessary subdirectories (approvals created on-demand)
const directories = [
workflowRoot,
PathUtils.getSpecPath(projectPath, ''),
PathUtils.getArchiveSpecsPath(projectPath),
PathUtils.getSteeringPath(projectPath),
PathUtils.getTemplatesPath(projectPath),
PathUtils.getAgentsPath(projectPath),
PathUtils.getCommandsPath(projectPath)
];
for (const dir of directories) {
await ensureDirectoryExists(dir);
}
return workflowRoot;
}
//# sourceMappingURL=path-utils.js.map