UNPKG

@codepack/workflow-cli

Version:

AI-powered workflow management CLI for developers

1,463 lines (1,436 loc) 706 kB
#!/usr/bin/env node "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var __privateWrapper = (obj, member, setter, getter) => ({ set _(value) { __privateSet(obj, member, value, setter); }, get _() { return __privateGet(obj, member, getter); } }); // ../../node_modules/.pnpm/tsup@8.5.0_jiti@1.21.7_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/cjs_shims.js var init_cjs_shims = __esm({ "../../node_modules/.pnpm/tsup@8.5.0_jiti@1.21.7_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/cjs_shims.js"() { "use strict"; } }); // src/utils/logger.ts var import_picocolors, ConsoleLogger, logger; var init_logger = __esm({ "src/utils/logger.ts"() { "use strict"; init_cjs_shims(); import_picocolors = __toESM(require("picocolors")); ConsoleLogger = class { constructor() { this.isDebug = process.env.DEBUG === "true"; } info(message, ...args) { console.log(import_picocolors.default.blue("\u2139"), message, ...args); } success(message, ...args) { console.log(import_picocolors.default.green("\u2713"), message, ...args); } warn(message, ...args) { console.warn(import_picocolors.default.yellow("\u26A0"), message, ...args); } error(message, ...args) { console.error(import_picocolors.default.red("\u2716"), message, ...args); } debug(message, ...args) { if (this.isDebug) { console.log(import_picocolors.default.gray("\u25CF"), import_picocolors.default.gray(message), ...args); } } log(message, ...args) { console.log(message, ...args); } }; logger = new ConsoleLogger(); } }); // src/utils/markdown.ts function parseMarkdown(content) { const lines = content.split("\n"); const sections = []; const stack = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const headerMatch = line.match(/^(#+)\s+(.+)/); if (headerMatch) { const level = headerMatch[1].length; const title = headerMatch[2]; const section = { title, level, content: [], subsections: [] }; while (stack.length > 0 && stack[stack.length - 1].level >= level) { stack.pop(); } if (stack.length === 0) { sections.push(section); } else { stack[stack.length - 1].subsections.push(section); } stack.push(section); } else if (stack.length > 0) { stack[stack.length - 1].content.push(line); } } return sections; } function parseTasks(content) { const lines = content.split("\n"); const tasks = []; let currentSection = ""; let taskId = 1; for (const line of lines) { const headerMatch = line.match(/^#+\s+(.+)/); if (headerMatch) { currentSection = headerMatch[1]; continue; } const taskMatch = line.match(/^-\s*\[([ x])\]\s*(.+)/); if (taskMatch) { const isCompleted = taskMatch[1] === "x"; const content2 = taskMatch[2]; let priority = "medium"; if (currentSection.toLowerCase().includes("high") || content2.includes("\u{1F534}")) { priority = "high"; } else if (currentSection.toLowerCase().includes("low") || content2.includes("\u{1F7E2}")) { priority = "low"; } let status = isCompleted ? "completed" : "pending"; if (currentSection.toLowerCase().includes("progress") || content2.includes("\u{1F504}")) { status = "in_progress"; } tasks.push({ id: `task-${taskId++}`, content: content2.replace(/[🔴🟡🟢🔄]/g, "").trim(), status, priority, section: currentSection }); } } return tasks; } function generateTaskList(tasks, groupBy = "status") { const lines = []; const now = /* @__PURE__ */ new Date(); lines.push("# \u{1F4CB} Task Management"); lines.push(""); lines.push(`> Last updated: ${now.toLocaleString()}`); lines.push(""); if (groupBy === "status") { const statuses = { in_progress: { title: "In Progress", emoji: "\u{1F504}" }, pending: { title: "Pending", emoji: "\u{1F4CB}" }, completed: { title: "Completed", emoji: "\u2705" }, cancelled: { title: "Cancelled", emoji: "\u274C" } }; for (const [status, info] of Object.entries(statuses)) { const statusTasks = tasks.filter((t) => t.status === status); if (statusTasks.length > 0) { lines.push(`## ${info.emoji} ${info.title} (${statusTasks.length})`); lines.push(""); statusTasks.forEach((task) => { const checkbox = task.status === "completed" ? "[x]" : "[ ]"; const priority = task.priority === "high" ? "\u{1F534}" : task.priority === "low" ? "\u{1F7E2}" : "\u{1F7E1}"; lines.push(`- ${checkbox} ${priority} ${task.content}`); }); lines.push(""); } } } else { const priorities = { high: { title: "High Priority", emoji: "\u{1F534}" }, medium: { title: "Medium Priority", emoji: "\u{1F7E1}" }, low: { title: "Low Priority", emoji: "\u{1F7E2}" } }; for (const [priority, info] of Object.entries(priorities)) { const priorityTasks = tasks.filter((t) => t.priority === priority && t.status !== "completed"); if (priorityTasks.length > 0) { lines.push(`## ${info.emoji} ${info.title} (${priorityTasks.length})`); lines.push(""); priorityTasks.forEach((task) => { const checkbox = task.status === "completed" ? "[x]" : "[ ]"; const status = task.status === "in_progress" ? "\u{1F504}" : ""; lines.push(`- ${checkbox} ${status} ${task.content}`); }); lines.push(""); } } } const total = tasks.length; const completed = tasks.filter((t) => t.status === "completed").length; const completionRate = total > 0 ? Math.round(completed / total * 100) : 0; lines.push("## \u{1F4CA} Statistics"); lines.push(""); lines.push(`- **Total Tasks**: ${total}`); lines.push(`- **Completed**: ${completed} (${completionRate}%)`); lines.push(`- **In Progress**: ${tasks.filter((t) => t.status === "in_progress").length}`); lines.push(`- **Pending**: ${tasks.filter((t) => t.status === "pending").length}`); return lines.join("\n"); } var init_markdown = __esm({ "src/utils/markdown.ts"() { "use strict"; init_cjs_shims(); } }); // src/core/workflow-runner.ts var import_path, import_fs_extra, import_child_process, import_util, import_js_yaml, execAsync, WorkflowRunner; var init_workflow_runner = __esm({ "src/core/workflow-runner.ts"() { "use strict"; init_cjs_shims(); import_path = __toESM(require("path")); import_fs_extra = __toESM(require("fs-extra")); import_child_process = require("child_process"); import_util = require("util"); import_js_yaml = __toESM(require("js-yaml")); init_logger(); init_markdown(); execAsync = (0, import_util.promisify)(import_child_process.exec); WorkflowRunner = class { constructor(projectRoot = process.cwd()) { this.projectRoot = projectRoot; this.workflowDir = import_path.default.join(projectRoot, ".workflow"); } async runCommand(commandName) { const commandPath = import_path.default.join(this.workflowDir, "commands", `${commandName}.md`); if (!await import_fs_extra.default.pathExists(commandPath)) { throw new Error(`Command "${commandName}" not found. Available: init, todo, context, review`); } logger.info(`\u{1F680} Running command: ${commandName}`); logger.info(""); const content = await import_fs_extra.default.readFile(commandPath, "utf-8"); const sections = parseMarkdown(content); for (const section of sections) { this.processSection(section); } } processSection(section, indent = 0) { const prefix = " ".repeat(indent); logger.info(`${prefix}${"#".repeat(section.level)} ${section.title}`); for (const line of section.content) { if (line.trim().startsWith("```")) { continue; } const inCodeBlock = section.content.some( (l, i) => i < section.content.indexOf(line) && l.trim().startsWith("```") && !section.content.slice(i + 1, section.content.indexOf(line)).some((l2) => l2.trim().startsWith("```")) ); if (inCodeBlock && line.trim()) { this.executeCommand(line.trim()); } else if (line.trim()) { logger.info(`${prefix}${line}`); } } section.subsections.forEach((sub) => this.processSection(sub, indent + 1)); } async executeCommand(command) { if (command.startsWith("#") || command.startsWith("//")) { return; } logger.info(` $ ${command}`); try { const { stdout, stderr } = await execAsync(command, { cwd: this.projectRoot, timeout: 3e4 // 30 second timeout }); if (stdout) { stdout.split("\n").forEach((line) => { if (line.trim()) logger.info(` ${line}`); }); } if (stderr && !stderr.includes("warning")) { stderr.split("\n").forEach((line) => { if (line.trim()) logger.warn(` ${line}`); }); } } catch (error) { logger.error(` Command failed: ${error.message}`); } } async startWorkflow(type, options = {}) { const workflowPath = import_path.default.join(this.workflowDir, "workflows", `${type}.yaml`); if (!await import_fs_extra.default.pathExists(workflowPath)) { const available = await this.listAvailableWorkflows(); throw new Error(`Workflow "${type}" not found. Available: ${available.join(", ")}`); } const workflowContent = await import_fs_extra.default.readFile(workflowPath, "utf-8"); const workflow = import_js_yaml.default.load(workflowContent); logger.info(`\u{1F504} Starting ${workflow.name}`); if (workflow.description) { logger.info(` ${workflow.description}`); } logger.info(""); const state = { type, name: options.name || `${type}-${Date.now()}`, startedAt: /* @__PURE__ */ new Date(), currentPhase: 0, phases: workflow.phases.map((phase) => ({ name: phase.name, status: "pending", completedTasks: [] })) }; await this.saveState(state); logger.info(`\u{1F4CB} Phase 1/${workflow.phases.length}: ${workflow.phases[0].name}`); logger.info(""); workflow.phases[0].tasks.forEach((task, index) => { logger.info(` ${index + 1}. ${task}`); }); logger.info(""); logger.info('Use "workflow status" to track progress'); logger.info('Use "workflow complete" to mark tasks as done'); } async getStatus() { const statePath = import_path.default.join(this.workflowDir, ".current-workflow.json"); if (!await import_fs_extra.default.pathExists(statePath)) { return null; } const state = await import_fs_extra.default.readJson(statePath); state.startedAt = new Date(state.startedAt); return state; } async listAvailableWorkflows() { const workflowsDir = import_path.default.join(this.workflowDir, "workflows"); if (!await import_fs_extra.default.pathExists(workflowsDir)) { return []; } const files = await import_fs_extra.default.readdir(workflowsDir); return files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => f.replace(/\.(yaml|yml)$/, "")); } async saveState(state) { const statePath = import_path.default.join(this.workflowDir, ".current-workflow.json"); await import_fs_extra.default.writeJson(statePath, state, { spaces: 2 }); } }; } }); // src/core/task-analyzer.ts var TaskAnalyzer; var init_task_analyzer = __esm({ "src/core/task-analyzer.ts"() { "use strict"; init_cjs_shims(); init_markdown(); TaskAnalyzer = class { constructor(options = {}) { this.enableAI = options.enableAI || false; this.apiKey = options.apiKey; } async analyze(content) { const tasks = parseTasks(content); const statistics = this.calculateStatistics(tasks); const insights = this.generateInsights(tasks); const recommendations = await this.generateRecommendations(tasks, insights); return { tasks, insights, recommendations, statistics }; } calculateStatistics(tasks) { const total = tasks.length; const completed = tasks.filter((t) => t.status === "completed").length; const inProgress = tasks.filter((t) => t.status === "in_progress").length; const pending = tasks.filter((t) => t.status === "pending").length; const completionRate = total > 0 ? Math.round(completed / total * 100) : 0; return { total, completed, inProgress, pending, completionRate }; } generateInsights(tasks) { const trends = []; const bottlenecks = []; const suggestions = []; const highPriorityCount = tasks.filter((t) => t.priority === "high" && t.status !== "completed").length; if (highPriorityCount > 5) { trends.push(`High number of incomplete high-priority tasks (${highPriorityCount})`); } const inProgressCount = tasks.filter((t) => t.status === "in_progress").length; if (inProgressCount > 3) { bottlenecks.push(`${inProgressCount} tasks in progress - consider completing some before starting new ones`); } const sections = new Set(tasks.map((t) => t.section).filter(Boolean)); if (sections.size > 1) { const sectionCounts = Array.from(sections).map((section) => ({ section, count: tasks.filter((t) => t.section === section).length })); const maxSection = sectionCounts.reduce((a, b) => a.count > b.count ? a : b); trends.push(`Most tasks in "${maxSection.section}" section (${maxSection.count} tasks)`); } if (tasks.length > 20 && tasks.filter((t) => t.status === "completed").length < 5) { suggestions.push("Consider breaking down large tasks into smaller, more manageable subtasks"); } const testTasks = tasks.filter((t) => t.content.toLowerCase().includes("test")); if (testTasks.length > 0 && testTasks.every((t) => t.status !== "completed")) { suggestions.push("Prioritize completing test-related tasks to ensure code quality"); } return { trends, bottlenecks, suggestions }; } async generateRecommendations(tasks, insights) { const recommendations = []; const urgentTasks = tasks.filter((t) => t.priority === "high" && t.status === "pending"); if (urgentTasks.length > 0) { recommendations.push({ type: "task", priority: "high", title: "Complete high-priority tasks", description: `You have ${urgentTasks.length} high-priority tasks pending. Focus on these first.`, action: urgentTasks[0].content }); } const inProgressTasks = tasks.filter((t) => t.status === "in_progress"); if (inProgressTasks.length > 3) { recommendations.push({ type: "workflow", priority: "medium", title: "Reduce work in progress", description: "Too many tasks in progress can reduce productivity. Complete current tasks before starting new ones.", action: "Review and complete in-progress tasks" }); } const hour = (/* @__PURE__ */ new Date()).getHours(); if (hour < 12) { recommendations.push({ type: "improvement", priority: "low", title: "Morning productivity tip", description: "Start with high-priority or challenging tasks while your energy is high", action: "Focus on complex tasks" }); } else if (hour > 16) { recommendations.push({ type: "improvement", priority: "low", title: "End-of-day recommendation", description: "Good time for code reviews, documentation, and planning tomorrow's tasks", action: "Review and document today's work" }); } if (this.enableAI && this.apiKey) { recommendations.push({ type: "improvement", priority: "medium", title: "AI Analysis Available", description: "Enable AI features for smarter task prioritization and insights", action: "Configure AI settings" }); } return recommendations; } async suggestNextTask(tasks) { const actionableTasks = tasks.filter( (t) => t.status === "pending" || t.status === "in_progress" ); if (actionableTasks.length === 0) { return null; } const inProgressTasks = actionableTasks.filter((t) => t.status === "in_progress"); if (inProgressTasks.length > 0) { return inProgressTasks.sort((a, b) => { const priorityOrder = { high: 0, medium: 1, low: 2 }; return priorityOrder[a.priority] - priorityOrder[b.priority]; })[0]; } const pendingTasks = actionableTasks.filter((t) => t.status === "pending"); return pendingTasks.sort((a, b) => { const priorityOrder = { high: 0, medium: 1, low: 2 }; return priorityOrder[a.priority] - priorityOrder[b.priority]; })[0]; } }; } }); // src/core/task-watcher.ts var import_chokidar, import_path2, import_fs_extra2, TaskWatcher; var init_task_watcher = __esm({ "src/core/task-watcher.ts"() { "use strict"; init_cjs_shims(); import_chokidar = require("chokidar"); import_path2 = __toESM(require("path")); import_fs_extra2 = __toESM(require("fs-extra")); init_task_analyzer(); init_markdown(); init_logger(); TaskWatcher = class { constructor(options) { this.watcher = null; this.lastSync = null; this.syncTimer = null; this.options = { syncInterval: 3e5, // 5 minutes default ...options }; this.analyzer = new TaskAnalyzer(); } async start() { if (this.watcher) { logger.warn("Task watcher is already running"); return; } await this.sync(); this.watcher = (0, import_chokidar.watch)(this.options.todoPath, { persistent: true, ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: 1e3, pollInterval: 100 } }); this.watcher.on("change", async () => { logger.info("\u{1F4DD} TODO file changed, syncing tasks..."); await this.sync(); }); this.watcher.on("error", (error) => { logger.error("Watch error:", error); }); if (this.options.syncInterval > 0) { this.syncTimer = setInterval( () => this.sync(), this.options.syncInterval ); } } async stop() { if (this.watcher) { await this.watcher.close(); this.watcher = null; } if (this.syncTimer) { clearInterval(this.syncTimer); this.syncTimer = null; } } async syncOnce() { await this.sync(); } async sync() { try { const todoContent = await import_fs_extra2.default.readFile(this.options.todoPath, "utf-8"); const analysis = await this.analyzer.analyze(todoContent); const dynamicContent = this.generateDynamicContent(analysis); const todoCommandPath = import_path2.default.join(this.options.workflowDir, "commands", "todo.md"); await import_fs_extra2.default.writeFile(todoCommandPath, dynamicContent); await this.updateActivityLog( `Synced ${analysis.statistics.total} tasks (${analysis.statistics.completed} completed)` ); this.lastSync = /* @__PURE__ */ new Date(); logger.debug(`Tasks synced at ${this.lastSync.toLocaleTimeString()}`); } catch (error) { logger.error("Failed to sync tasks:", error.message); } } generateDynamicContent(analysis) { const lines = []; const now = /* @__PURE__ */ new Date(); lines.push("# \u{1F4CB} Dynamic Task Management"); lines.push(""); lines.push(`> Last synced: ${now.toLocaleString()}`); lines.push(`> Source: ${import_path2.default.relative(this.options.projectRoot, this.options.todoPath)}`); lines.push(""); if (analysis.recommendations.length > 0) { lines.push("## \u{1F916} AI Recommendations"); lines.push(""); analysis.recommendations.forEach((rec) => { lines.push(`### ${rec.title}`); lines.push(`${rec.description}`); if (rec.action) { lines.push(`**Action**: ${rec.action}`); } lines.push(""); }); } if (analysis.insights.trends.length > 0 || analysis.insights.bottlenecks.length > 0 || analysis.insights.suggestions.length > 0) { lines.push("## \u{1F4A1} Insights"); lines.push(""); if (analysis.insights.trends.length > 0) { lines.push("### Trends"); analysis.insights.trends.forEach((trend) => { lines.push(`- ${trend}`); }); lines.push(""); } if (analysis.insights.bottlenecks.length > 0) { lines.push("### Bottlenecks"); analysis.insights.bottlenecks.forEach((bottleneck) => { lines.push(`- ${bottleneck}`); }); lines.push(""); } if (analysis.insights.suggestions.length > 0) { lines.push("### Suggestions"); analysis.insights.suggestions.forEach((suggestion) => { lines.push(`- ${suggestion}`); }); lines.push(""); } } lines.push(generateTaskList(analysis.tasks)); lines.push(""); lines.push("## \u{1F680} Quick Actions"); lines.push(""); lines.push("```bash"); lines.push("# Sync with latest TODO file"); lines.push("workflow sync"); lines.push(""); lines.push("# Start watching for changes"); lines.push("workflow sync --watch"); lines.push(""); lines.push("# Start a new feature"); lines.push("workflow start feature"); lines.push("```"); return lines.join("\n"); } async updateActivityLog(message) { const logPath = import_path2.default.join(this.options.workflowDir, "logs", "activity.log"); await import_fs_extra2.default.ensureDir(import_path2.default.dirname(logPath)); const timestamp = (/* @__PURE__ */ new Date()).toISOString(); const logEntry = `[${timestamp}] ${message} `; await import_fs_extra2.default.appendFile(logPath, logEntry); } }; } }); // src/core/storage/index.ts var import_fs_extra3, import_path3, import_crypto, FileStorage, MemoryStorage, EncryptedStorage, LayeredStorage; var init_storage = __esm({ "src/core/storage/index.ts"() { "use strict"; init_cjs_shims(); import_fs_extra3 = __toESM(require("fs-extra")); import_path3 = __toESM(require("path")); import_crypto = __toESM(require("crypto")); FileStorage = class { constructor(basePath) { this.basePath = basePath; import_fs_extra3.default.ensureDirSync(this.basePath); } getFilePath(key) { const safeKey = key.replace(/[^a-zA-Z0-9.-]/g, "_"); return import_path3.default.join(this.basePath, `${safeKey}.json`); } async get(key) { const filePath = this.getFilePath(key); if (!await import_fs_extra3.default.pathExists(filePath)) { return void 0; } try { const content = await import_fs_extra3.default.readFile(filePath, "utf-8"); return JSON.parse(content); } catch (error) { throw new Error(`Failed to read storage key "${key}": ${error.message}`); } } async set(key, value) { const filePath = this.getFilePath(key); try { await import_fs_extra3.default.writeJson(filePath, value, { spaces: 2 }); } catch (error) { throw new Error(`Failed to write storage key "${key}": ${error.message}`); } } async delete(key) { const filePath = this.getFilePath(key); if (await import_fs_extra3.default.pathExists(filePath)) { await import_fs_extra3.default.remove(filePath); } } async has(key) { const filePath = this.getFilePath(key); return import_fs_extra3.default.pathExists(filePath); } async list(pattern) { try { const files = await import_fs_extra3.default.readdir(this.basePath); const jsonFiles = files.filter((f) => f.endsWith(".json")); let keys = jsonFiles.map((f) => f.replace(".json", "").replace(/_/g, ".")); if (pattern) { const regex = new RegExp(pattern); keys = keys.filter((k) => regex.test(k)); } return keys; } catch (error) { throw new Error(`Failed to list storage keys: ${error.message}`); } } async clear() { await import_fs_extra3.default.emptyDir(this.basePath); } }; MemoryStorage = class { constructor() { this.store = /* @__PURE__ */ new Map(); } async get(key) { return this.store.get(key); } async set(key, value) { this.store.set(key, value); } async delete(key) { this.store.delete(key); } async has(key) { return this.store.has(key); } async list(pattern) { let keys = Array.from(this.store.keys()); if (pattern) { const regex = new RegExp(pattern); keys = keys.filter((k) => regex.test(k)); } return keys; } async clear() { this.store.clear(); } }; EncryptedStorage = class { constructor(storage, secretKey) { this.storage = storage; this.secretKey = secretKey; const key = import_crypto.default.scryptSync(secretKey, "salt", 32); const iv = Buffer.alloc(16, 0); this.cipher = import_crypto.default.createCipheriv("aes-256-cbc", key, iv); this.decipher = import_crypto.default.createDecipheriv("aes-256-cbc", key, iv); } encrypt(data) { const key = import_crypto.default.scryptSync(this.secretKey, "salt", 32); const iv = import_crypto.default.randomBytes(16); const cipher = import_crypto.default.createCipheriv("aes-256-cbc", key, iv); let encrypted = cipher.update(data, "utf8", "hex"); encrypted += cipher.final("hex"); return iv.toString("hex") + ":" + encrypted; } decrypt(data) { const parts = data.split(":"); const iv = Buffer.from(parts[0], "hex"); const encrypted = parts[1]; const key = import_crypto.default.scryptSync(this.secretKey, "salt", 32); const decipher = import_crypto.default.createDecipheriv("aes-256-cbc", key, iv); let decrypted = decipher.update(encrypted, "hex", "utf8"); decrypted += decipher.final("utf8"); return decrypted; } async get(key) { const encryptedData = await this.storage.get(key); if (!encryptedData) { return void 0; } try { const decrypted = this.decrypt(encryptedData); return JSON.parse(decrypted); } catch (error) { throw new Error(`Failed to decrypt storage key "${key}": ${error.message}`); } } async set(key, value) { const data = JSON.stringify(value); const encrypted = this.encrypt(data); await this.storage.set(key, encrypted); } async delete(key) { await this.storage.delete(key); } async has(key) { return this.storage.has(key); } async list(pattern) { return this.storage.list(pattern); } async clear() { await this.storage.clear(); } }; LayeredStorage = class { constructor(layers) { this.layers = layers; if (layers.length === 0) { throw new Error("LayeredStorage requires at least one storage layer"); } } async get(key) { for (const layer of this.layers) { if (await layer.has(key)) { return layer.get(key); } } return void 0; } async set(key, value) { await this.layers[0].set(key, value); } async delete(key) { await Promise.all(this.layers.map((layer) => layer.delete(key))); } async has(key) { for (const layer of this.layers) { if (await layer.has(key)) { return true; } } return false; } async list(pattern) { const allKeys = /* @__PURE__ */ new Set(); for (const layer of this.layers) { const keys = await layer.list(pattern); keys.forEach((k) => allKeys.add(k)); } return Array.from(allKeys); } async clear() { await Promise.all(this.layers.map((layer) => layer.clear())); } }; } }); // src/core/config-manager.ts var import_zod, import_events, import_path4, import_fs_extra4, ConfigManager, defaultSchemas; var init_config_manager = __esm({ "src/core/config-manager.ts"() { "use strict"; init_cjs_shims(); import_zod = require("zod"); import_events = require("events"); import_path4 = __toESM(require("path")); import_fs_extra4 = __toESM(require("fs-extra")); init_storage(); init_logger(); ConfigManager = class extends import_events.EventEmitter { constructor(options = {}) { super(); this.options = options; this.configs = /* @__PURE__ */ new Map(); this.schemas = /* @__PURE__ */ new Map(); this.watchers = /* @__PURE__ */ new Map(); // 配置层优先级(从高到低) this.layerPriority = [ "runtime" /* RUNTIME */, "environment" /* ENVIRONMENT */, "user" /* USER */, "project" /* PROJECT */, "system" /* SYSTEM */, "defaults" /* DEFAULTS */ ]; this.storage = this.initializeStorage(); if (options.schemas) { Object.entries(options.schemas).forEach(([key, schema]) => { this.schemas.set(key, schema); }); } this.layerPriority.forEach((layer) => { this.configs.set(layer, {}); }); } /** * 初始化存储 */ initializeStorage() { if (this.options.storage) { return this.options.storage; } const projectRoot = this.options.projectRoot || process.cwd(); const configDir = import_path4.default.join(projectRoot, ".workflow", "config"); const layers = [ new MemoryStorage(), // runtime 层 new MemoryStorage(), // environment 层 new FileStorage(import_path4.default.join(configDir, "user")), new FileStorage(import_path4.default.join(configDir, "project")), new FileStorage(import_path4.default.join(configDir, "system")), new MemoryStorage() // defaults 层 ]; let storage = new LayeredStorage(layers); if (this.options.encryption?.enabled) { const secretKey = this.options.encryption.secretKey || process.env.WORKFLOW_SECRET_KEY; if (!secretKey) { throw new Error("Encryption enabled but no secret key provided"); } storage = new EncryptedStorage(storage, secretKey); } return storage; } /** * 加载配置 */ async load() { this.loadEnvironmentVariables(); for (const layer of this.layerPriority) { if (layer === "environment" /* ENVIRONMENT */ || layer === "runtime" /* RUNTIME */) { continue; } try { const config = await this.storage.get(layer) || {}; this.configs.set(layer, config); } catch (error) { logger.warn(`Failed to load ${layer} config:`, error); } } if (this.options.watch) { this.startWatching(); } } /** * 加载环境变量 */ loadEnvironmentVariables() { const envConfig = {}; Object.entries(process.env).forEach(([key, value]) => { if (key.startsWith("WORKFLOW_")) { const configKey = key.substring("WORKFLOW_".length).toLowerCase().replace(/_/g, "."); const keys = configKey.split("."); let current = envConfig; for (let i = 0; i < keys.length - 1; i++) { if (!(keys[i] in current)) { current[keys[i]] = {}; } current = current[keys[i]]; } try { current[keys[keys.length - 1]] = JSON.parse(value); } catch { current[keys[keys.length - 1]] = value; } } }); this.configs.set("environment" /* ENVIRONMENT */, envConfig); } /** * 获取配置值 */ get(key, defaultValue) { for (const layer of this.layerPriority) { const layerConfig = this.configs.get(layer); if (layerConfig && this.hasNested(layerConfig, key)) { return this.getNested(layerConfig, key); } } return defaultValue; } /** * 设置配置值 */ async set(key, value, layer = "runtime" /* RUNTIME */) { if (this.schemas.has(key)) { const schema = this.schemas.get(key); try { value = schema.parse(value); } catch (error) { if (error instanceof import_zod.ZodError) { throw new Error(`Validation failed for "${key}": ${error.errors[0].message}`); } throw error; } } const layerConfig = this.configs.get(layer) || {}; const oldValue = this.getNested(layerConfig, key); this.setNested(layerConfig, key, value); this.configs.set(layer, layerConfig); if (layer !== "runtime" /* RUNTIME */ && layer !== "environment" /* ENVIRONMENT */) { await this.storage.set(layer, layerConfig); } const event = { key, oldValue, newValue: value, layer }; this.emit("change", event); this.emit(`change:${key}`, event); const handlers = this.watchers.get(key); if (handlers) { for (const handler of handlers) { try { await handler(event); } catch (error) { logger.error(`Config change handler error for "${key}":`, error); } } } } /** * 删除配置值 */ async delete(key, layer) { if (layer) { const layerConfig = this.configs.get(layer) || {}; this.deleteNested(layerConfig, key); this.configs.set(layer, layerConfig); if (layer !== "runtime" /* RUNTIME */ && layer !== "environment" /* ENVIRONMENT */) { await this.storage.set(layer, layerConfig); } } else { for (const l of this.layerPriority) { await this.delete(key, l); } } } /** * 验证配置 */ validate(schema) { const config = this.getAll(); const schemaToUse = schema || this.createCombinedSchema(); try { schemaToUse.parse(config); return { valid: true }; } catch (error) { if (error instanceof import_zod.ZodError) { return { valid: false, errors: error.errors.map((e) => ({ path: e.path.join("."), message: e.message })) }; } throw error; } } /** * 监听配置变化 */ watch(pattern, callback) { const regex = new RegExp(pattern); const handler = (event) => { if (regex.test(event.key)) { callback(event); } }; if (!this.watchers.has(pattern)) { this.watchers.set(pattern, /* @__PURE__ */ new Set()); } this.watchers.get(pattern).add(callback); this.on("change", handler); return () => { this.off("change", handler); const handlers = this.watchers.get(pattern); if (handlers) { handlers.delete(callback); if (handlers.size === 0) { this.watchers.delete(pattern); } } }; } /** * 导出配置 */ export(layer) { if (layer) { return { ...this.configs.get(layer) || {} }; } return this.getAll(); } /** * 导入配置 */ async import(config, layer = "runtime" /* RUNTIME */) { const processObject = async (obj, prefix = "") => { for (const [key, value] of Object.entries(obj)) { const fullKey = prefix ? `${prefix}.${key}` : key; if (value && typeof value === "object" && !Array.isArray(value)) { await processObject(value, fullKey); } else { try { await this.set(fullKey, value, layer); } catch (error) { throw new Error(`Import validation failed: ${error.message}`); } } } }; await processObject(config); } /** * 获取所有配置(合并所有层) */ getAll() { const result = {}; for (let i = this.layerPriority.length - 1; i >= 0; i--) { const layer = this.layerPriority[i]; const layerConfig = this.configs.get(layer) || {}; Object.assign(result, layerConfig); } return result; } /** * 重置配置 */ async reset(layer) { if (layer) { this.configs.set(layer, {}); if (layer !== "runtime" /* RUNTIME */ && layer !== "environment" /* ENVIRONMENT */) { await this.storage.delete(layer); } } else { for (const l of this.layerPriority) { await this.reset(l); } } } /** * 启动文件监听 */ startWatching() { const configDir = import_path4.default.join(this.options.projectRoot || process.cwd(), ".workflow", "config"); if (!import_fs_extra4.default.existsSync(configDir)) { return; } this.fileWatcher = import_fs_extra4.default.watch(configDir, { recursive: true }, async (eventType, filename) => { if (!filename || !filename.endsWith(".json")) { return; } logger.debug(`Config file changed: ${filename}`); try { await this.load(); this.emit("reload"); } catch (error) { logger.error("Failed to reload config:", error); } }); } /** * 停止文件监听 */ stopWatching() { if (this.fileWatcher) { this.fileWatcher.close(); this.fileWatcher = void 0; } } /** * 清理资源 */ async dispose() { this.stopWatching(); this.removeAllListeners(); this.watchers.clear(); } // 辅助方法 hasNested(obj, path17) { const keys = path17.split("."); let current = obj; for (const key of keys) { if (!current || typeof current !== "object" || !(key in current)) { return false; } current = current[key]; } return true; } getNested(obj, path17) { const keys = path17.split("."); let current = obj; for (const key of keys) { if (current == null || typeof current !== "object") { return void 0; } current = current[key]; } return current; } setNested(obj, path17, value) { const keys = path17.split("."); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!(key in current) || typeof current[key] !== "object") { current[key] = {}; } current = current[key]; } current[keys[keys.length - 1]] = value; } deleteNested(obj, path17) { const keys = path17.split("."); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!(key in current)) { return; } current = current[key]; } delete current[keys[keys.length - 1]]; } createCombinedSchema() { const shape = {}; this.schemas.forEach((schema, key) => { shape[key] = schema; }); return import_zod.z.object(shape).partial(); } createSchemaForObject(obj) { return import_zod.z.object({}).passthrough(); } }; defaultSchemas = { "project.name": import_zod.z.string().min(1), "project.version": import_zod.z.string().regex(/^\d+\.\d+\.\d+$/), "settings.autoSync": import_zod.z.boolean(), "settings.syncInterval": import_zod.z.number().min(1e3), "settings.enableAI": import_zod.z.boolean(), "settings.checkMode": import_zod.z.enum(["quick", "normal", "full"]) }; } }); // src/utils/config.ts async function getConfigManager(projectRoot) { if (!configManager) { configManager = new ConfigManager({ projectRoot: projectRoot || process.cwd(), schemas: { ...defaultSchemas, workflow: workflowConfigSchema }, encryption: { enabled: !!process.env.WORKFLOW_ENCRYPTION_ENABLED, secretKey: process.env.WORKFLOW_SECRET_KEY }, watch: process.env.NODE_ENV !== "test" }); await configManager.load(); } return configManager; } async function loadConfig(workflowDir) { const manager = await getConfigManager(workflowDir); const config = manager.get("workflow"); if (!config) { throw new Error('Workflow configuration not found. Run "workflow init" first.'); } return config; } async function saveConfig(workflowDir, config) { const manager = await getConfigManager(workflowDir); await manager.set("workflow", config, "project" /* PROJECT */); } var import_zod2, configManager, workflowConfigSchema; var init_config = __esm({ "src/utils/config.ts"() { "use strict"; init_cjs_shims(); init_config_manager(); import_zod2 = require("zod"); configManager = null; workflowConfigSchema = import_zod2.z.object({ version: import_zod2.z.string().regex(/^\d+\.\d+\.\d+$/, "Version must be in format x.x.x"), project: import_zod2.z.object({ name: import_zod2.z.string(), type: import_zod2.z.string(), framework: import_zod2.z.array(import_zod2.z.string()), language: import_zod2.z.string() }), settings: import_zod2.z.object({ autoSync: import_zod2.z.boolean(), syncInterval: import_zod2.z.number().min(1e3), enableAI: import_zod2.z.boolean(), checkMode: import_zod2.z.enum(["quick", "normal", "full"]) }), paths: import_zod2.z.object({ todoFile: import_zod2.z.string().optional(), workflowDir: import_zod2.z.string().optional() }).optional() }); } }); // src/templates/index.ts var WorkflowTemplates; var init_templates = __esm({ "src/templates/index.ts"() { "use strict"; init_cjs_shims(); WorkflowTemplates = class { static getInitCommand(context) { return `# \u{1F680} Project Initialization ## Status Check \`\`\`bash git status pwd ls -la \`\`\` ## Environment \`\`\`bash node --version npm --version \`\`\` ## Dependencies \`\`\`bash npm install \`\`\` ## Quick Start - Development: \`npm run dev\` - Build: \`npm run build\` - Test: \`npm test\` `; } static getTodoCommand() { return `# \u{1F4CB} Task Management > Auto-synced from project TODO files ## High Priority _Tasks will be synced here_ ## In Progress _Tasks will be synced here_ ## Completed _Tasks will be synced here_ --- Run \`workflow sync\` to update tasks. `; } static getContextCommand(projectName) { return `# \u{1F3D7}\uFE0F Project Context ## Overview Project: ${projectName} ## Structure \`\`\` src/ \u251C\u2500\u2500 components/ \u251C\u2500\u2500 utils/ \u2514\u2500\u2500 index.ts \`\`\` ## Commands \`\`\`bash npm run dev npm test npm run build \`\`\` `; } static getReviewCommand() { return `# \u{1F50D} Code Review Checklist ## Before Commit - [ ] Code follows style guide - [ ] Tests pass - [ ] No debug code - [ ] Documentation updated ## Security - [ ] No hardcoded secrets - [ ] Input validation - [ ] Dependencies updated ## Performance - [ ] No unnecessary renders - [ ] Optimized queries - [ ] Bundle size checked `; } static getFeatureWorkflow() { return `name: Feature Development description: Workflow for developing new features phases: - name: Planning tasks: - Define requirements - Create design - Break down tasks - name: Implementation tasks: - Create branch - Implement feature - Write tests - name: Review tasks: - Self review - Create PR - Address feedback `; } static getBugfixWorkflow() { return `name: Bug Fix description: Workflow for fixing bugs phases: - name: Investigation tasks: - Reproduce issue - Find root cause - name: Fix tasks: - Implement fix - Add tests - name: Verify tasks: - Test fix - Check regressions `; } static getRefactorWorkflow() { return `name: Refactoring description: Workflow for code refactoring phases: - name: Analysis tasks: - Identify targets - Plan approach - name: Refactor tasks: - Refactor code - Update tests - name: Validate tasks: - Run tests - Check behavior `; } }; } }); // src/constants.ts var WORKFLOW_DIR, KNOWLEDGE_DIR