UNPKG

md-linear-sync

Version:

Sync Linear tickets to local markdown files with status-based folder organization

122 lines (120 loc) 4.98 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigManager = void 0; exports.getWorkflowTypeOrder = getWorkflowTypeOrder; exports.stateNameToFolderName = stateNameToFolderName; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); class ConfigManager { static async loadConfig(projectPath = process.cwd()) { const configPath = path_1.default.join(projectPath, this.CONFIG_FILE); if (!fs_1.default.existsSync(configPath)) { throw new Error(`Configuration file ${this.CONFIG_FILE} not found. Run 'md-linear-sync init' to create it.`); } try { const configContent = fs_1.default.readFileSync(configPath, 'utf-8'); const config = JSON.parse(configContent); this.validateConfig(config); return config; } catch (error) { throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); } } static async saveConfig(config, projectPath = process.cwd()) { const configPath = path_1.default.join(projectPath, this.CONFIG_FILE); this.validateConfig(config); try { const configContent = JSON.stringify(config, null, 2); fs_1.default.writeFileSync(configPath, configContent, 'utf-8'); } catch (error) { throw new Error(`Failed to save configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); } } static createDefaultConfig() { return { teamId: '', teamName: '', projectId: '', projectName: '', statusMapping: { 'Todo': { id: '', folder: 'todo' }, 'In Progress': { id: '', folder: 'in-progress' }, 'In Review': { id: '', folder: 'in-review' }, 'Backlog': { id: '', folder: 'backlog' }, 'Done': { id: '', folder: 'done' }, 'Cancelled': { id: '', folder: 'cancelled' } }, labelMapping: {}, timezone: 'Asia/Singapore', lastUpdated: new Date().toISOString() }; } static createEnvExample(projectPath = process.cwd()) { const envExamplePath = path_1.default.join(projectPath, this.ENV_EXAMPLE_FILE); const envContent = `# Linear API Configuration LINEAR_API_KEY=your_linear_api_key_here # Optional: Slack Notifications SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/slack/webhook # Optional: Webhook Security WEBHOOK_SECRET=your_webhook_secret_here `; fs_1.default.writeFileSync(envExamplePath, envContent, 'utf-8'); } static loadEnvironmentConfig() { const linearApiKey = process.env.LINEAR_API_KEY; if (!linearApiKey) { throw new Error('LINEAR_API_KEY environment variable is required'); } return { linear: { apiKey: linearApiKey, teamId: '', // Will be populated from config file webhookSecret: process.env.WEBHOOK_SECRET }, slack: { webhookUrl: process.env.SLACK_WEBHOOK_URL } }; } static validateConfig(config) { if (!config.teamId) { throw new Error('teamId is required in configuration'); } if (!config.statusMapping) { throw new Error('statusMapping is required in configuration'); } if (!config.timezone) { throw new Error('timezone is required in configuration'); } // Validate status mapping structure for (const [stateName, mapping] of Object.entries(config.statusMapping)) { if (!mapping.id || !mapping.folder) { throw new Error(`statusMapping for '${stateName}' must have both 'id' and 'folder' properties`); } // Type is optional for backward compatibility with existing configs } } } exports.ConfigManager = ConfigManager; ConfigManager.CONFIG_FILE = '.linear-sync.json'; ConfigManager.ENV_EXAMPLE_FILE = '.env.example'; // Define the desired workflow order const WORKFLOW_TYPE_ORDER = ['started', 'unstarted', 'backlog', 'completed', 'canceled']; function getWorkflowTypeOrder(type) { const index = WORKFLOW_TYPE_ORDER.indexOf(type); return index === -1 ? 999 : index; // Unknown types go to the end } function stateNameToFolderName(stateName, type, typeOrder, stateIndex) { const baseName = stateName .toLowerCase() .replace(/\s+/g, '-') .replace(/[^a-z0-9-]/g, ''); // Add numbered prefix with subcategory: major.minor-name return `${typeOrder + 1}.${stateIndex + 1}-${baseName}`; } //# sourceMappingURL=index.js.map