@codepack/workflow-cli
Version:
AI-powered workflow management CLI for developers
1,463 lines (1,436 loc) • 706 kB
JavaScript
#!/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