UNPKG

@bobmatnyc/ai-trackdown-tools

Version:

Professional CLI tool for ai-trackdown functionality with comprehensive PR management system

1,478 lines (1,432 loc) 1.04 MB
#!/usr/bin/env node var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // node_modules/tsup/assets/esm_shims.js import path from "path"; import { fileURLToPath } from "url"; var init_esm_shims = __esm({ "node_modules/tsup/assets/esm_shims.js"() { "use strict"; } }); // src/utils/unified-path-resolver.ts import { existsSync } from "fs"; import { join } from "path"; var _UnifiedPathResolver, UnifiedPathResolver; var init_unified_path_resolver = __esm({ "src/utils/unified-path-resolver.ts"() { "use strict"; init_esm_shims(); _UnifiedPathResolver = class _UnifiedPathResolver { config; projectRoot; cliTasksDir; // CLI override via --tasks-dir or --root-dir constructor(config, projectRoot, cliTasksDir) { this.config = config; this.projectRoot = projectRoot; this.cliTasksDir = cliTasksDir; } /** * Get the tasks root directory with proper priority resolution: * 1. CLI option override (--root-dir, --tasks-dir) * 2. Environment variable (AITRACKDOWN_TASKS_DIR) * 3. Config file setting (tasks_directory) * 4. Default to "tasks" */ getTasksRootDirectory() { if (this.cliTasksDir) { return this.cliTasksDir; } const envTasksDir = process.env.AITRACKDOWN_TASKS_DIR || process.env.AITRACKDOWN_ROOT_DIR; if (envTasksDir) { return envTasksDir; } if (this.config.tasks_directory) { return this.config.tasks_directory; } return "tasks"; } /** * Get all unified paths following the required structure */ getUnifiedPaths() { const tasksRoot = this.getTasksRootDirectory(); return { projectRoot: this.projectRoot, configDir: join(this.projectRoot, ".ai-trackdown"), tasksRoot: join(this.projectRoot, tasksRoot), epicsDir: join(this.projectRoot, tasksRoot, this.config.structure.epics_dir), issuesDir: join(this.projectRoot, tasksRoot, this.config.structure.issues_dir), tasksDir: join(this.projectRoot, tasksRoot, this.config.structure.tasks_dir), prsDir: join(this.projectRoot, tasksRoot, this.config.structure.prs_dir || "prs"), templatesDir: join(this.projectRoot, tasksRoot, this.config.structure.templates_dir) }; } /** * Get path for specific item type */ getItemTypeDirectory(type) { const paths = this.getUnifiedPaths(); switch (type) { case "project": return join(paths.tasksRoot, "projects"); case "epic": return paths.epicsDir; case "issue": return paths.issuesDir; case "task": return paths.tasksDir; case "pr": return paths.prsDir; default: throw new Error(`Unknown item type: ${type}`); } } /** * Get all directories that should be created for the unified structure */ getRequiredDirectories() { const paths = this.getUnifiedPaths(); return [ paths.configDir, paths.tasksRoot, paths.epicsDir, paths.issuesDir, paths.tasksDir, paths.prsDir, paths.templatesDir ]; } /** * Check if legacy directory structure exists (separate root directories) */ detectLegacyStructure() { const legacyDirs = []; const suggestions = []; const potentialLegacyDirs = [ join(this.projectRoot, "epics"), join(this.projectRoot, "issues"), join(this.projectRoot, "tasks"), join(this.projectRoot, "prs"), join(this.projectRoot, "trackdown") // Old trackdown structure ]; for (const dir of potentialLegacyDirs) { if (existsSync(dir)) { legacyDirs.push(dir); } } if (legacyDirs.length > 0) { const tasksRoot = this.getTasksRootDirectory(); suggestions.push( `# Detected legacy directory structure. Migration options:`, ``, `# Option 1: Use CLI override to maintain current structure`, `export AITRACKDOWN_TASKS_DIR="" # Use project root`, ``, `# Option 2: Migrate to unified structure`, `mkdir -p ${tasksRoot}`, ...legacyDirs.map((dir) => { const dirName = dir.split("/").pop(); return `mv ${dirName} ${tasksRoot}/${dirName} 2>/dev/null || true`; }), ``, `# Option 3: Update configuration`, `# Edit .ai-trackdown/config.yaml and set:`, `# tasks_directory: "" # Use project root` ); } return { hasLegacy: legacyDirs.length > 0, legacyDirs, suggestions }; } /** * Get migration commands for moving to unified structure */ getMigrationCommands() { const legacy = this.detectLegacyStructure(); if (!legacy.hasLegacy) { return []; } return legacy.suggestions; } /** * Validate current directory structure */ validateStructure() { const _paths = this.getUnifiedPaths(); const issues = []; const missingDirs = []; const requiredDirs = this.getRequiredDirectories(); for (const dir of requiredDirs) { if (!existsSync(dir)) { missingDirs.push(dir); } } const legacy = this.detectLegacyStructure(); if (legacy.hasLegacy) { issues.push(`Legacy directory structure detected: ${legacy.legacyDirs.join(", ")}`); } return { valid: issues.length === 0 && missingDirs.length === 0, issues, missingDirs }; } /** * Update CLI tasks directory override */ setCliTasksDir(tasksDir) { this.cliTasksDir = tasksDir; } /** * Clear CLI tasks directory override */ clearCliTasksDir() { this.cliTasksDir = void 0; } /** * Show structure information for debugging */ showStructureInfo() { const paths = this.getUnifiedPaths(); const validation = this.validateStructure(); console.log(` \u{1F3D7}\uFE0F AI-Trackdown Directory Structure`); console.log(`\u{1F4C1} Tasks Root: ${paths.tasksRoot}`); console.log(` \u251C\u2500\u2500 \u{1F4C2} epics/ \u2192 ${paths.epicsDir}`); console.log(` \u251C\u2500\u2500 \u{1F4C2} issues/ \u2192 ${paths.issuesDir}`); console.log(` \u251C\u2500\u2500 \u{1F4C2} tasks/ \u2192 ${paths.tasksDir}`); console.log(` \u251C\u2500\u2500 \u{1F4C2} prs/ \u2192 ${paths.prsDir}`); console.log(` \u2514\u2500\u2500 \u{1F4C2} templates/ \u2192 ${paths.templatesDir}`); if (validation.missingDirs.length > 0) { console.log(` \u26A0\uFE0F Missing directories:`); validation.missingDirs.forEach((dir) => console.log(` \u2022 ${dir}`)); } if (validation.issues.length > 0) { console.log(` \u{1F6A8} Issues detected:`); validation.issues.forEach((issue) => console.log(` \u2022 ${issue}`)); } const legacy = this.detectLegacyStructure(); if (legacy.hasLegacy) { console.log(` \u{1F4CB} Migration suggestions:`); legacy.suggestions.forEach((suggestion) => console.log(` ${suggestion}`)); } } }; __name(_UnifiedPathResolver, "UnifiedPathResolver"); UnifiedPathResolver = _UnifiedPathResolver; } }); // src/utils/template-manager.ts var template_manager_exports = {}; __export(template_manager_exports, { TemplateManager: () => TemplateManager }); import * as fs from "fs"; import * as path2 from "path"; import { fileURLToPath as fileURLToPath2 } from "url"; import * as YAML from "yaml"; var __filename2, __dirname2, _TemplateManager, TemplateManager; var init_template_manager = __esm({ "src/utils/template-manager.ts"() { "use strict"; init_esm_shims(); __filename2 = fileURLToPath2(import.meta.url); __dirname2 = path2.dirname(__filename2); _TemplateManager = class _TemplateManager { bundledTemplatesDir; constructor() { const possiblePaths = [ path2.join(__dirname2, "../../templates"), // Development: src/utils -> templates path2.join(__dirname2, "../templates"), // Compiled: dist/utils -> dist/templates path2.join(__dirname2, "templates"), // Compiled: dist -> dist/templates path2.resolve(__dirname2, "..", "templates") // Alternative dist structure ]; this.bundledTemplatesDir = possiblePaths.find((dir) => { try { return fs.existsSync(dir); } catch { return false; } }) || path2.join(__dirname2, "../../templates"); } /** * Get the path to bundled templates */ getBundledTemplatesDir() { return this.bundledTemplatesDir; } /** * Check if bundled templates exist */ hasBundledTemplates() { return fs.existsSync(this.bundledTemplatesDir); } /** * List all bundled template files */ listBundledTemplates() { if (!this.hasBundledTemplates()) { return []; } try { return fs.readdirSync(this.bundledTemplatesDir).filter((file) => file.endsWith(".yaml")).sort(); } catch (error) { console.warn( `Failed to list bundled templates: ${error instanceof Error ? error.message : "Unknown error"}` ); return []; } } /** * Deploy bundled templates to project's templates directory */ deployTemplates(projectTemplatesDir, force = false) { if (!this.hasBundledTemplates()) { console.warn("No bundled templates found. Creating default templates programmatically."); this.createDefaultTemplates(projectTemplatesDir, force); return; } if (!fs.existsSync(projectTemplatesDir)) { fs.mkdirSync(projectTemplatesDir, { recursive: true }); } const bundledFiles = this.listBundledTemplates(); let deployedCount = 0; for (const templateFile of bundledFiles) { const sourcePath = path2.join(this.bundledTemplatesDir, templateFile); const destPath = path2.join(projectTemplatesDir, templateFile); try { if (fs.existsSync(destPath) && !force) { console.log(`\u23ED\uFE0F Skipping ${templateFile} (already exists)`); continue; } fs.copyFileSync(sourcePath, destPath); console.log(`\u2705 Deployed ${templateFile}`); deployedCount++; } catch (error) { console.error( `\u274C Failed to deploy ${templateFile}: ${error instanceof Error ? error.message : "Unknown error"}` ); } } console.log(`\u{1F4E6} Deployed ${deployedCount} template(s) to ${projectTemplatesDir}`); } /** * Create default templates programmatically if bundled templates are not available */ createDefaultTemplates(projectTemplatesDir, force = false) { if (!fs.existsSync(projectTemplatesDir)) { fs.mkdirSync(projectTemplatesDir, { recursive: true }); } const defaultTemplates = [ { type: "epic", name: "default", description: "Default epic template", frontmatter_template: { title: "Epic Title", description: "Epic description", status: "planning", priority: "medium", assignee: "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: [ "context/requirements", "context/constraints", "context/assumptions", "context/dependencies" ], sync_status: "local" }, content_template: `# Epic: {{title}} ## Overview {{description}} ## Objectives - [ ] Objective 1 - [ ] Objective 2 - [ ] Objective 3 ## Acceptance Criteria - [ ] Criteria 1 - [ ] Criteria 2 ## Related Issues {{#related_issues}} - {{.}} {{/related_issues}} ## Notes Add any additional notes here.` }, { type: "issue", name: "default", description: "Default issue template", frontmatter_template: { title: "Issue Title", description: "Issue description", status: "planning", priority: "medium", assignee: "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: [ "context/requirements", "context/constraints", "context/assumptions", "context/dependencies" ], sync_status: "local" }, content_template: `# Issue: {{title}} ## Description {{description}} ## Tasks {{#related_tasks}} - [ ] {{.}} {{/related_tasks}} ## Acceptance Criteria - [ ] Criteria 1 - [ ] Criteria 2 ## Notes Add any additional notes here.` }, { type: "task", name: "default", description: "Default task template", frontmatter_template: { title: "Task Title", description: "Task description", status: "planning", priority: "medium", assignee: "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: [ "context/requirements", "context/constraints", "context/assumptions", "context/dependencies" ], sync_status: "local" }, content_template: `# Task: {{title}} ## Description {{description}} ## Steps 1. Step 1 2. Step 2 3. Step 3 ## Acceptance Criteria - [ ] Criteria 1 - [ ] Criteria 2 ## Notes Add any additional notes here.` }, { type: "pr", name: "default", description: "Default PR template", frontmatter_template: { title: "PR Title", description: "PR description", status: "planning", priority: "medium", assignee: "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: [ "context/requirements", "context/constraints", "context/assumptions", "context/dependencies" ], sync_status: "local" }, content_template: `# PR: {{title}} ## Description {{description}} ## Changes - Change 1 - Change 2 - Change 3 ## Testing - [ ] Unit tests pass - [ ] Integration tests pass - [ ] Manual testing completed ## Checklist - [ ] Code follows style guidelines - [ ] Self-review completed - [ ] Documentation updated - [ ] Tests added/updated ## Related - Issue: {{issue_id}} - Branch: {{branch_name}} - Target: {{target_branch}} ## Notes Add any additional notes here.` } ]; let deployedCount = 0; for (const template of defaultTemplates) { const templatePath = path2.join(projectTemplatesDir, `${template.type}-${template.name}.yaml`); try { if (fs.existsSync(templatePath) && !force) { console.log(`\u23ED\uFE0F Skipping ${template.type}-${template.name}.yaml (already exists)`); continue; } const templateContent = YAML.stringify(template, { indent: 2, lineWidth: 120 }); fs.writeFileSync(templatePath, templateContent, "utf8"); console.log(`\u2705 Created ${template.type}-${template.name}.yaml`); deployedCount++; } catch (error) { console.error( `\u274C Failed to create ${template.type}-${template.name}.yaml: ${error instanceof Error ? error.message : "Unknown error"}` ); } } console.log(`\u{1F4E6} Created ${deployedCount} default template(s) in ${projectTemplatesDir}`); } /** * Get template by type and name, with fallback to bundled templates */ getTemplate(projectTemplatesDir, type, name = "default") { const templateFileName = `${type}-${name}.yaml`; const projectTemplatePath = path2.join(projectTemplatesDir, templateFileName); if (fs.existsSync(projectTemplatePath)) { try { const templateContent = fs.readFileSync(projectTemplatePath, "utf8"); return YAML.parse(templateContent); } catch (error) { console.warn( `Failed to load project template ${projectTemplatePath}: ${error instanceof Error ? error.message : "Unknown error"}` ); } } if (this.hasBundledTemplates()) { const bundledTemplatePath = path2.join(this.bundledTemplatesDir, templateFileName); if (fs.existsSync(bundledTemplatePath)) { try { const templateContent = fs.readFileSync(bundledTemplatePath, "utf8"); return YAML.parse(templateContent); } catch (error) { console.warn( `Failed to load bundled template ${bundledTemplatePath}: ${error instanceof Error ? error.message : "Unknown error"}` ); } } } return null; } /** * Validate template file structure */ validateTemplate(templatePath) { try { const content = fs.readFileSync(templatePath, "utf8"); const template = YAML.parse(content); const requiredFields = [ "type", "name", "description", "frontmatter_template", "content_template" ]; for (const field of requiredFields) { if (!template[field]) { console.error(`Template ${templatePath} missing required field: ${field}`); return false; } } const validTypes = ["epic", "issue", "task", "pr"]; if (!validTypes.includes(template.type)) { console.error(`Template ${templatePath} has invalid type: ${template.type}`); return false; } return true; } catch (error) { console.error( `Failed to validate template ${templatePath}: ${error instanceof Error ? error.message : "Unknown error"}` ); return false; } } }; __name(_TemplateManager, "TemplateManager"); TemplateManager = _TemplateManager; } }); // src/utils/config-manager.ts var config_manager_exports = {}; __export(config_manager_exports, { ConfigManager: () => ConfigManager }); import * as fs2 from "fs"; import * as path3 from "path"; import * as YAML2 from "yaml"; var DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE, _ConfigManager, ConfigManager; var init_config_manager = __esm({ "src/utils/config-manager.ts"() { "use strict"; init_esm_shims(); init_unified_path_resolver(); DEFAULT_CONFIG_DIR = ".ai-trackdown"; DEFAULT_CONFIG_FILE = "config.yaml"; _ConfigManager = class _ConfigManager { configPath; config = null; constructor(projectRoot) { const root = projectRoot || this.findProjectRoot(); this.configPath = path3.join(root, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE); } /** * Load configuration from file */ loadConfig() { var _a; if (this.config) { return this.config; } if (!fs2.existsSync(this.configPath)) { throw new Error( `AI-Trackdown configuration not found at ${this.configPath}. Run 'aitrackdown init' to create a new project.` ); } try { const configContent = fs2.readFileSync(this.configPath, "utf8"); const rawConfig = YAML2.parse(configContent); if (((_a = rawConfig.project) == null ? void 0 : _a.name) && !rawConfig.name) { this.config = { name: rawConfig.project.name, description: rawConfig.project.description, version: rawConfig.version || "1.0.0", tasks_directory: rawConfig.tasks_directory || "tasks", structure: rawConfig.structure || { epics_dir: "epics", issues_dir: "issues", tasks_dir: "tasks", templates_dir: "templates", prs_dir: "prs" }, naming_conventions: rawConfig.naming_conventions || { epic_prefix: "EP", issue_prefix: "ISS", task_prefix: "TSK", pr_prefix: "PR", file_extension: ".md" }, default_assignee: rawConfig.default_assignee || "unassigned", ai_context_templates: rawConfig.ai_context_templates || [], automation: rawConfig.automation || { auto_update_timestamps: true, auto_calculate_tokens: false, auto_sync_status: true } }; } else { this.config = rawConfig; } this.validateConfig(this.config); this.normalizeConfig(this.config); return this.config; } catch (error) { throw new Error( `Failed to load AI-Trackdown configuration: ${error instanceof Error ? error.message : "Unknown error"}` ); } } /** * Save configuration to file */ saveConfig(config) { this.validateConfig(config); const configDir = path3.dirname(this.configPath); if (!fs2.existsSync(configDir)) { fs2.mkdirSync(configDir, { recursive: true }); } const yamlContent = YAML2.stringify(config, { indent: 2, lineWidth: 120, minContentWidth: 20 }); fs2.writeFileSync(this.configPath, yamlContent, "utf8"); this.config = config; } /** * Create default configuration */ createDefaultConfig(projectName, options = {}) { const defaultConfig = { name: projectName, description: options.description || `AI-Trackdown project: ${projectName}`, version: "1.0.0", // NEW: Default tasks directory for unified structure tasks_directory: options.tasks_directory || "tasks", structure: { epics_dir: "epics", issues_dir: "issues", tasks_dir: "tasks", templates_dir: "templates", prs_dir: "prs" // NEW: PR directory }, naming_conventions: { epic_prefix: "EP", issue_prefix: "ISS", task_prefix: "TSK", pr_prefix: "PR", // NEW: PR prefix file_extension: ".md" }, default_assignee: options.default_assignee || "unassigned", ai_context_templates: [ "context/requirements", "context/constraints", "context/assumptions", "context/dependencies" ], automation: { auto_update_timestamps: true, auto_calculate_tokens: false, auto_sync_status: true }, ...options }; return defaultConfig; } /** * Initialize new project with default structure */ initializeProject(projectName, options = {}) { const config = this.createDefaultConfig(projectName, options); this.createProjectStructure(config); this.createDefaultTemplates(config); this.saveConfig(config); return config; } /** * Initialize new project with structure only (no template creation) */ initializeProjectStructure(projectName, options = {}) { const config = this.createDefaultConfig(projectName, options); this.createProjectStructure(config); this.saveConfig(config); return config; } /** * Update specific configuration values */ updateConfig(updates) { const currentConfig = this.loadConfig(); const updatedConfig = this.deepMerge(currentConfig, updates); this.saveConfig(updatedConfig); return updatedConfig; } /** * Get configuration with environment overrides */ getConfig() { const config = this.loadConfig(); if (process.env.ATD_DEFAULT_ASSIGNEE) { config.default_assignee = process.env.ATD_DEFAULT_ASSIGNEE; } if (process.env.ATD_AUTO_TIMESTAMPS === "false") { config.automation.auto_update_timestamps = false; } if (process.env.ATD_AUTO_CALCULATE_TOKENS === "true") { config.automation.auto_calculate_tokens = true; } return config; } /** * Get absolute paths for project structure using unified directory layout */ getAbsolutePaths(cliTasksDir) { const config = this.getConfig(); const projectRoot = path3.dirname(path3.dirname(this.configPath)); const pathResolver = new UnifiedPathResolver(config, projectRoot, cliTasksDir); const unifiedPaths = pathResolver.getUnifiedPaths(); return { projectRoot: unifiedPaths.projectRoot, configDir: unifiedPaths.configDir, tasksRoot: unifiedPaths.tasksRoot, epicsDir: unifiedPaths.epicsDir, issuesDir: unifiedPaths.issuesDir, tasksDir: unifiedPaths.tasksDir, prsDir: unifiedPaths.prsDir, templatesDir: unifiedPaths.templatesDir }; } /** * Check if current directory is an AI-Trackdown project */ isProjectDirectory(dir) { const checkDir = dir || process.cwd(); const configPath = path3.join(checkDir, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE); return fs2.existsSync(configPath); } /** * Find project root by walking up directory tree */ findProjectRoot(startDir) { let currentDir = startDir || process.cwd(); while (currentDir !== path3.dirname(currentDir)) { const configPath = path3.join(currentDir, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE); if (fs2.existsSync(configPath)) { return currentDir; } currentDir = path3.dirname(currentDir); } return startDir || process.cwd(); } /** * Validate configuration structure */ validateConfig(config) { const required = ["name", "version", "structure", "naming_conventions"]; for (const field of required) { if (!config[field]) { throw new Error(`Configuration missing required field: ${field}`); } } const structureFields = ["epics_dir", "issues_dir", "tasks_dir", "templates_dir"]; for (const field of structureFields) { if (!config.structure[field]) { throw new Error(`Configuration structure missing required field: ${field}`); } } const namingFields = ["epic_prefix", "issue_prefix", "task_prefix", "file_extension"]; for (const field of namingFields) { if (!config.naming_conventions[field]) { throw new Error(`Configuration naming_conventions missing required field: ${field}`); } } } /** * Normalize configuration (ensure defaults and proper types) */ normalizeConfig(config) { if (!config.automation) { config.automation = { auto_update_timestamps: true, auto_calculate_tokens: false, auto_sync_status: true }; } if (!config.ai_context_templates) { config.ai_context_templates = []; } config.structure.epics_dir = config.structure.epics_dir.replace(/^\/|\/$/g, ""); config.structure.issues_dir = config.structure.issues_dir.replace(/^\/|\/$/g, ""); config.structure.tasks_dir = config.structure.tasks_dir.replace(/^\/|\/$/g, ""); config.structure.templates_dir = config.structure.templates_dir.replace(/^\/|\/$/g, ""); if (!config.naming_conventions.file_extension.startsWith(".")) { config.naming_conventions.file_extension = `.${config.naming_conventions.file_extension}`; } } /** * Create project directory structure using unified layout */ createProjectStructure(config) { const projectRoot = path3.dirname(path3.dirname(this.configPath)); const pathResolver = new UnifiedPathResolver(config, projectRoot); const requiredDirs = pathResolver.getRequiredDirectories(); for (const dir of requiredDirs) { if (!fs2.existsSync(dir)) { fs2.mkdirSync(dir, { recursive: true }); } } } /** * Create default templates */ createDefaultTemplates(config) { const projectRoot = path3.dirname(path3.dirname(this.configPath)); const pathResolver = new UnifiedPathResolver(config, projectRoot); const paths = pathResolver.getUnifiedPaths(); const templatesDir = paths.templatesDir; const templates = [ { type: "epic", name: "default", description: "Default epic template", frontmatter_template: { title: "Epic Title", description: "Epic description", status: "planning", priority: "medium", assignee: config.default_assignee || "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: config.ai_context_templates || [], sync_status: "local" }, content_template: `# Epic: {{title}} ## Overview {{description}} ## Objectives - [ ] Objective 1 - [ ] Objective 2 - [ ] Objective 3 ## Acceptance Criteria - [ ] Criteria 1 - [ ] Criteria 2 ## Related Issues {{#related_issues}} - {{.}} {{/related_issues}} ## Notes Add any additional notes here.` }, { type: "issue", name: "default", description: "Default issue template", frontmatter_template: { title: "Issue Title", description: "Issue description", status: "planning", priority: "medium", assignee: config.default_assignee || "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: config.ai_context_templates || [], sync_status: "local" }, content_template: `# Issue: {{title}} ## Description {{description}} ## Tasks {{#related_tasks}} - [ ] {{.}} {{/related_tasks}} ## Acceptance Criteria - [ ] Criteria 1 - [ ] Criteria 2 ## Notes Add any additional notes here.` }, { type: "task", name: "default", description: "Default task template", frontmatter_template: { title: "Task Title", description: "Task description", status: "planning", priority: "medium", assignee: config.default_assignee || "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: config.ai_context_templates || [], sync_status: "local" }, content_template: `# Task: {{title}} ## Description {{description}} ## Steps 1. Step 1 2. Step 2 3. Step 3 ## Acceptance Criteria - [ ] Criteria 1 - [ ] Criteria 2 ## Notes Add any additional notes here.` }, { type: "pr", name: "default", description: "Default PR template", frontmatter_template: { title: "PR Title", description: "PR description", status: "planning", priority: "medium", assignee: config.default_assignee || "unassigned", created_date: "", updated_date: "", estimated_tokens: 0, actual_tokens: 0, ai_context: config.ai_context_templates || [], sync_status: "local" }, content_template: `# PR: {{title}} ## Description {{description}} ## Changes - Change 1 - Change 2 - Change 3 ## Testing - [ ] Unit tests pass - [ ] Integration tests pass - [ ] Manual testing completed ## Checklist - [ ] Code follows style guidelines - [ ] Self-review completed - [ ] Documentation updated - [ ] Tests added/updated ## Related - Issue: {{issue_id}} - Branch: {{branch_name}} - Target: {{target_branch}} ## Notes Add any additional notes here.` } ]; for (const template of templates) { const templatePath = path3.join(templatesDir, `${template.type}-${template.name}.yaml`); if (!fs2.existsSync(templatePath)) { const templateContent = YAML2.stringify(template, { indent: 2, lineWidth: 120 }); fs2.writeFileSync(templatePath, templateContent, "utf8"); } } } /** * Deep merge two objects */ deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) { result[key] = this.deepMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } /** * Get template by type and name */ getTemplate(type, name = "default") { const config = this.getConfig(); const projectRoot = path3.dirname(path3.dirname(this.configPath)); const pathResolver = new UnifiedPathResolver(config, projectRoot); const paths = pathResolver.getUnifiedPaths(); const templatesDir = paths.templatesDir; const templatePath = path3.join(templatesDir, `${type}-${name}.yaml`); if (!fs2.existsSync(templatePath)) { return null; } try { const templateContent = fs2.readFileSync(templatePath, "utf8"); return YAML2.parse(templateContent); } catch (error) { console.warn( `Failed to load template ${templatePath}: ${error instanceof Error ? error.message : "Unknown error"}` ); return null; } } /** * Get template by type and name with fallback to bundled templates */ getTemplateWithFallback(type, name = "default") { const config = this.getConfig(); const projectRoot = path3.dirname(path3.dirname(this.configPath)); const pathResolver = new UnifiedPathResolver(config, projectRoot); const paths = pathResolver.getUnifiedPaths(); const templatesDir = paths.templatesDir; const projectTemplate = this.getTemplate(type, name); if (projectTemplate) { return projectTemplate; } const TemplateManager2 = (init_template_manager(), __toCommonJS(template_manager_exports)).TemplateManager; const templateManager = new TemplateManager2(); return templateManager.getTemplate(templatesDir, type, name); } /** * List available templates */ listTemplates() { const config = this.getConfig(); const projectRoot = path3.dirname(path3.dirname(this.configPath)); const pathResolver = new UnifiedPathResolver(config, projectRoot); const paths = pathResolver.getUnifiedPaths(); const templatesDir = paths.templatesDir; if (!fs2.existsSync(templatesDir)) { return []; } const templates = []; const files = fs2.readdirSync(templatesDir).filter((file) => file.endsWith(".yaml")); for (const file of files) { try { const templateContent = fs2.readFileSync(path3.join(templatesDir, file), "utf8"); const template = YAML2.parse(templateContent); templates.push({ type: template.type, name: template.name, description: template.description }); } catch (error) { console.warn( `Failed to parse template ${file}: ${error instanceof Error ? error.message : "Unknown error"}` ); } } return templates; } }; __name(_ConfigManager, "ConfigManager"); ConfigManager = _ConfigManager; } }); // src/utils/colors.ts import chalk from "chalk"; var colors, _ColorTheme, ColorTheme; var init_colors = __esm({ "src/utils/colors.ts"() { "use strict"; init_esm_shims(); colors = { // Main brand color - cyan for professional tech feel primary: chalk.cyan, // Success operations and positive feedback success: chalk.green, // Warnings and cautionary messages warning: chalk.yellow, // Errors and critical issues error: chalk.red, // Informational messages and tips info: chalk.blue, // Secondary text and less important information muted: chalk.gray, // Important highlights and emphasis highlight: chalk.bold.white }; _ColorTheme = class _ColorTheme { // Priority-based colors static priority(level) { switch (level.toLowerCase()) { case "low": return chalk.gray; case "medium": return chalk.yellow; case "high": return chalk.magenta; case "critical": return chalk.red.bold; default: return chalk.whiteBright; } } // Status-based colors static status(status) { switch (status.toLowerCase()) { case "todo": return chalk.gray; case "in-progress": return chalk.blue; case "done": return chalk.green; case "blocked": return chalk.red; default: return chalk.whiteBright; } } // Command-specific colors static command(command) { return chalk.cyan.bold(command); } // Option colors static option(option) { return chalk.yellow(option); } // Argument colors static argument(arg) { return chalk.green(arg); } // Header styling static header(text) { return chalk.bold.cyan(` ${text} ${"=".repeat(text.length)}`); } // Subheader styling static subheader(text) { return chalk.bold.white(` ${text} ${"-".repeat(text.length)}`); } // Badge styling for tags, labels, etc. static badge(text, variant = "info") { const colorFn = colors[variant]; return colorFn(` ${text} `); } // Create a bordered box for important messages static box(text, variant = "info") { const lines = text.split("\n"); const maxLength = Math.max(...lines.map((line) => line.length)); const colorFn = colors[variant]; const border = "\u2500".repeat(maxLength + 2); const top = `\u250C${border}\u2510`; const bottom = `\u2514${border}\u2518`; const content = lines.map((line) => `\u2502 ${line.padEnd(maxLength)} \u2502`).join("\n"); return colorFn(`${top} ${content} ${bottom}`); } // Progress indicators static progress(current, total) { const percentage = Math.round(current / total * 100); const filled = Math.round(current / total * 20); const empty = 20 - filled; const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty); if (percentage < 30) { return chalk.red(`[${bar}] ${percentage}%`); } else if (percentage < 70) { return chalk.yellow(`[${bar}] ${percentage}%`); } else { return chalk.green(`[${bar}] ${percentage}%`); } } // Create a separator line static separator(char = "\u2500", length = 50) { return chalk.gray(char.repeat(length)); } // Timestamp formatting static timestamp(date) { return chalk.dim(date.toISOString().replace("T", " ").substring(0, 19)); } // File path formatting static path(path40) { return chalk.cyan.underline(path40); } // Code formatting static code(code) { return chalk.gray.inverse(` ${code} `); } // URL formatting static url(url) { return chalk.blue.underline(url); } // Keyboard shortcut formatting static key(key) { return chalk.inverse(` ${key} `); } }; __name(_ColorTheme, "ColorTheme"); ColorTheme = _ColorTheme; } }); // src/utils/formatter.ts import boxen from "boxen"; import figlet from "figlet"; var _Formatter, Formatter; var init_formatter = __esm({ "src/utils/formatter.ts"() { "use strict"; init_esm_shims(); init_colors(); _Formatter = class _Formatter { static success(message) { return colors.success(`\u2705 ${message}`); } static error(message) { return colors.error(`\u274C ${message}`); } static warning(message) { return colors.warning(`\u26A0\uFE0F ${message}`); } static info(message) { return colors.info(`\u2139\uFE0F ${message}`); } static debug(message) { return colors.muted(`\u{1F50D} ${message}`); } static header(text) { return ColorTheme.header(text); } static subheader(text) { return ColorTheme.subheader(text); } static highlight(text) { return colors.highlight(text); } static dim(text) { return colors.muted(text); } // Enhanced banner for CLI startup static banner(text) { try { const ascii = figlet.textSync(text, { font: "ANSI Shadow", horizontalLayout: "default", verticalLayout: "default", width: 80, whitespaceBreak: true }); return colors.primary(ascii); } catch { return ColorTheme.header(text); } } // Create beautiful notification boxes static box(message, variant = "info") { const borderColors = { info: "cyan", success: "green", warning: "yellow", error: "red" }; return boxen(message, { padding: 1, margin: 1, borderStyle: "round", borderColor: borderColors[variant], backgroundColor: void 0 }); } // Professional item formatting with enhanced styling static formatItem(item, format = "detailed") { var _a, _b; const statusBadge = ColorTheme.badge( item.status.toUpperCase(), _Formatter.getStatusVariant(item.status) ); const priorityBadge = ColorTheme.badge( item.priority.toUpperCase(), _Formatter.getPriorityVariant(item.priority) ); if (format === "compact") { return [ `${statusBadge} ${priorityBadge} ${colors.highlight(item.title)}`, colors.muted(`ID: ${item.id}`), item.assignee ? colors.info(`@${item.assignee}`) : "", ((_a = item.tags) == null ? void 0 : _a.length) ? colors.muted(`[${item.tags.join(", ")}]`) : "" ].filter(Boolean).join(" "); } const sections = [ `${statusBadge} ${priorityBadge}`, colors.highlight(item.title), colors.muted(`ID: ${item.id}`), item.assignee ? colors.info(`\u{1F464} Assignee: ${item.assignee}`) : "", item.description ? colors.muted(`\u{1F4DD} ${item.description}`) : "", item.estimate ? colors.warning(`\u{1F4CA} ${item.estimate} story points`) : "", ((_b = item.tags) == null ? void 0 : _b.length) ? colors.primary(`\u{1F3F7}\uFE0F Tags: ${item.tags.join(", ")}`) : "", ColorTheme.timestamp(item.createdAt) ]; return sections.filter(Boolean).join("\n"); } // Enhanced list formatting with statistics static formatList(items, showStats = true) { if (items.length === 0) { return _Formatter.box("No items found", "info"); } const formatted = items.map((item, index) => { const prefix = colors.muted(`${(index + 1).toString().padStart(2, " ")}. `); return `${prefix}${_Formatter.formatItem(item, "compact")}`; }).join("\n"); if (!showStats) { return formatted; } const stats = _Formatter.generateStats(items); return `${formatted} ${stats}`; } // Generate item statistics static generateStats(items) { const total = items.length; const byStatus = _Formatter.groupBy(items, "status"); const byPriority = _Formatter.groupBy(items, "priority"); const statusSection = Object.entries(byStatus).map(([status, count]) => { const color = ColorTheme.status(status); return color(`${status}: ${count}`); }).join(" | "); const prioritySection = Object.entries(byPriority).map(([priority, count]) => { const color = ColorTheme.priority(priority); return color(`${priority}: ${count}`); }).join(" | "); return [ ColorTheme.separator(), colors.highlight(`\u{1F4CA} Statistics (${total} total)`), `Status: ${statusSection}`, `Priority: ${prioritySection}`, ColorTheme.separator() ].join("\n"); } // Enhanced table formatting static formatTable(items) { if (items.length === 0) { return _Formatter.box("No items found", "info"); } const headers = ["ID", "Title", "Status", "Priority", "Assignee", "Tags"]; const maxWidths = _Formatter.calculateColumnWidths(items, headers); const headerRow = headers.map((header, i) => colors.highlight(header.padEnd(maxWidths[i]))).join(" | "); const separator = ColorTheme.separator("\u2500", headerRow.length); const rows = items.map((item) => { var _a; const cells = [ item.id, item.title.length > 30 ? `${item.title.substring(0, 27)}...` : item.title, item.status, item.priority, item.assignee || "unassigned", ((_a = item.tags) == null ? void 0 : _a.join(", ")) || "" ]; return cells.map((cell, i) => { const colored = i === 2 ? ColorTheme.status(cell)(cell) : i === 3 ? ColorTheme.priority(cell)(cell) : cell; return colored.padEnd(maxWidths[i]); }).join(" | "); }); return [headerRow, separator, ...rows].join("\n");