@fromsvenwithlove/devops-issues-cli
Version:
AI-powered CLI tool and library for Azure DevOps work item management with Claude agents
205 lines (177 loc) • 4.97 kB
JavaScript
/**
* Templates Index
*
* Provides access to pre-built Azure DevOps work item templates
* and utilities for template management.
*/
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Load template from JSON file
*/
function loadTemplate(filename) {
try {
const content = readFileSync(join(__dirname, filename), 'utf-8');
return JSON.parse(content);
} catch (error) {
throw new Error(`Failed to load template ${filename}: ${error.message}`);
}
}
/**
* Available templates
*/
export const templates = {
userStory: {
name: 'User Story Template',
description: 'Basic user story with tasks and acceptance criteria',
file: 'user-story-template.json',
category: 'story',
get content() { return loadTemplate('user-story-template.json'); }
},
feature: {
name: 'Feature Template',
description: 'Feature with multiple user stories and tasks',
file: 'feature-template.json',
category: 'feature',
get content() { return loadTemplate('feature-template.json'); }
},
bug: {
name: 'Bug Report Template',
description: 'Detailed bug report with reproduction steps',
file: 'bug-template.json',
category: 'bug',
get content() { return loadTemplate('bug-template.json'); }
},
epic: {
name: 'Epic Template',
description: 'Large epic with features and stories',
file: 'epic-template.json',
category: 'epic',
get content() { return loadTemplate('epic-template.json'); }
},
// Project-specific templates
webApp: {
name: 'Web Application Project',
description: 'Complete web app project structure',
file: 'web-app-project.json',
category: 'project',
get content() { return loadTemplate('web-app-project.json'); }
},
api: {
name: 'API Project',
description: 'REST API development project structure',
file: 'api-project.json',
category: 'project',
get content() { return loadTemplate('api-project.json'); }
},
mobileApp: {
name: 'Mobile Application',
description: 'Mobile app development project structure',
file: 'mobile-app-project.json',
category: 'project',
get content() { return loadTemplate('mobile-app-project.json'); }
}
};
/**
* Get list of all available templates
*/
export function getTemplateList() {
return Object.keys(templates).map(key => ({
key,
...templates[key]
}));
}
/**
* Get template by key
*/
export function getTemplate(key) {
return templates[key] || null;
}
/**
* Get templates by category
*/
export function getTemplatesByCategory(category) {
return Object.keys(templates)
.filter(key => templates[key].category === category)
.map(key => ({
key,
...templates[key]
}));
}
/**
* Template categories
*/
export const categories = {
story: 'User Stories',
feature: 'Features',
epic: 'Epics',
bug: 'Bug Reports',
project: 'Project Templates'
};
/**
* Create a new work item from template
*/
export function createFromTemplate(templateKey, overrides = {}) {
const template = getTemplate(templateKey);
if (!template) {
throw new Error(`Template '${templateKey}' not found`);
}
const baseTemplate = template.content;
// Deep merge overrides
return mergeTemplate(baseTemplate, overrides);
}
/**
* Deep merge template with overrides
*/
function mergeTemplate(template, overrides) {
const result = JSON.parse(JSON.stringify(template)); // Deep clone
if (overrides.metadata) {
result.metadata = { ...result.metadata, ...overrides.metadata };
}
if (overrides.workItems) {
// Replace work items entirely if provided
result.workItems = overrides.workItems;
}
return result;
}
/**
* Validate template structure
*/
export function validateTemplate(template) {
const required = ['workItems'];
const missing = required.filter(field => !(field in template));
if (missing.length > 0) {
throw new Error(`Template missing required fields: ${missing.join(', ')}`);
}
if (!Array.isArray(template.workItems) || template.workItems.length === 0) {
throw new Error('Template must have at least one work item');
}
// Validate each work item has required fields
for (const [index, item] of template.workItems.entries()) {
if (!item.type || !item.title) {
throw new Error(`Work item ${index} missing required fields (type, title)`);
}
}
return true;
}
/**
* Load all templates for export
*/
export function loadAllTemplates() {
const result = {};
for (const [key, template] of Object.entries(templates)) {
try {
result[key] = {
...template,
content: template.content
};
} catch (error) {
console.warn(`Warning: Could not load template ${key}: ${error.message}`);
}
}
return result;
}
export default templates;