UNPKG

chorenzo

Version:

Open-source CLI engine that automates your engineering workflow with AI-powered workspace analysis and recipe application

1,586 lines (1,550 loc) 281 kB
#!/usr/bin/env node // src/main.ts import { Command } from "commander"; import { render } from "ink"; import React16 from "react"; // src/utils/error.utils.ts function extractErrorMessage(error) { return error instanceof Error ? error.message : String(error); } function formatErrorMessage(context, error) { const message = extractErrorMessage(error); return `${context}: ${message}`; } // src/utils/logger.utils.ts import * as fs3 from "fs"; import * as path3 from "path"; import pino from "pino"; // src/utils/workspace-config.utils.ts import * as fs2 from "fs"; import * as path2 from "path"; // src/utils/git.utils.ts import * as fs from "fs"; import * as path from "path"; function findGitRoot(startPath = process.cwd()) { let currentPath = path.resolve(startPath); while (currentPath !== path.dirname(currentPath)) { const gitPath = path.join(currentPath, ".git"); try { const stat = fs.statSync(gitPath); if (stat.isDirectory() || stat.isFile()) { return currentPath; } } catch (error) { if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") { throw error; } } currentPath = path.dirname(currentPath); } return process.cwd(); } function normalizeRepoIdentifier(gitUrl) { let normalized = gitUrl; if (normalized.startsWith("git@")) { normalized = normalized.replace(/^git@([^:]+):/, "$1/").replace(/\.git$/, ""); } else { normalized = normalized.replace(/^https?:\/\//, "").replace(/\.git$/, ""); } const match = normalized.match(/([^/]+)\/([^/]+\/[^/]+)$/); if (match?.[2]) { return match[2]; } return normalized; } // src/utils/workspace-config.utils.ts var WorkspaceConfig = class _WorkspaceConfig { static instance = null; workspaceRoot = null; constructor() { } static getInstance() { if (!_WorkspaceConfig.instance) { _WorkspaceConfig.instance = new _WorkspaceConfig(); } return _WorkspaceConfig.instance; } getWorkspaceRoot() { if (!this.workspaceRoot) { this.workspaceRoot = findGitRoot(); } return this.workspaceRoot; } getChorenzoDir() { const root = this.getWorkspaceRoot(); return path2.join(root, ".chorenzo"); } getAnalysisPath() { const chorenzoDir = this.getChorenzoDir(); return path2.join(chorenzoDir, "analysis.json"); } getStatePath() { const chorenzoDir = this.getChorenzoDir(); return path2.join(chorenzoDir, "state.json"); } getLogsDir() { const chorenzoDir = this.getChorenzoDir(); return path2.join(chorenzoDir, "logs"); } getLogPath() { const logsDir = this.getLogsDir(); return path2.join(logsDir, "chorenzo.log"); } getArchivedLogPath() { const logsDir = this.getLogsDir(); const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"); return path2.join(logsDir, `chorenzo-${timestamp}.log`); } ensureChorenzoDir() { const chorenzoDir = this.getChorenzoDir(); fs2.mkdirSync(chorenzoDir, { recursive: true }); } }; var workspaceConfig = WorkspaceConfig.getInstance(); // src/utils/logger.utils.ts var Logger = class { static instance = null; static MAX_LOG_SIZE_MB = 10; static rotateLogIfNeeded(logPath) { if (!fs3.existsSync(logPath)) { return; } const stats = fs3.statSync(logPath); const fileSizeMB = stats.size / (1024 * 1024); if (fileSizeMB > this.MAX_LOG_SIZE_MB) { const archivedPath = workspaceConfig.getArchivedLogPath(); fs3.renameSync(logPath, archivedPath); } } static initialize() { if (this.instance) { return; } const isTest = process.env["NODE_ENV"] === "test" || process.env["JEST_WORKER_ID"] !== void 0; if (isTest) { this.instance = pino({ level: "silent" }); return; } try { const logPath = workspaceConfig.getLogPath(); fs3.mkdirSync(path3.dirname(logPath), { recursive: true }); this.rotateLogIfNeeded(logPath); const logStream = pino.transport({ target: "pino-pretty", options: { destination: logPath, colorize: false, translateTime: "yyyy-mm-dd HH:MM:ss", ignore: "pid,hostname", append: true } }); this.instance = pino( { level: process.env["DEBUG"] ? "debug" : "info" }, logStream ); } catch (error) { throw new Error(formatErrorMessage("Failed to initialize Logger", error)); } } static getInstance() { if (!this.instance) { this.initialize(); } if (!this.instance) { throw new Error("Failed to initialize logger instance"); } return this.instance; } static debug(obj, msg) { this.getInstance().debug(obj, msg); } static info(obj, msg) { this.getInstance().info(obj, msg); } static warn(obj, msg) { this.getInstance().warn(obj, msg); } static error(obj, msg) { this.getInstance().error(obj, msg); } }; // package.json var package_default = { name: "chorenzo", version: "1.10.0", description: "Open-source CLI engine that automates your engineering workflow with AI-powered workspace analysis and recipe application", main: "dist/main.js", type: "module", bin: { chorenzo: "dist/main.js" }, scripts: { dev: "tsx watch src/main.ts", start: "tsx src/main.ts", build: "tsup", typecheck: "tsc --noEmit", format: "prettier --write .", "format:check": "prettier --check .", lint: "eslint .", "lint:fix": "eslint --fix .", test: "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js", "test:watch": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js --watch", "test:coverage": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js --coverage", prepare: "husky" }, repository: { type: "git", url: "git+https://github.com/chorenzo-dev/engine.git" }, keywords: [ "cli", "automation", "devtools", "ai", "typescript", "eslint", "cicd", "github-actions", "code-quality", "developer-experience", "workflow", "recipes", "engineering-automation", "claude-ai", "workspace-analysis", "monorepo", "project-setup", "developer-tools", "code-formatting", "linting", "testing-setup", "infrastructure-as-code", "configuration-management", "npx", "chorenzo" ], author: "Chorenzo Development Team", license: "Apache-2.0", homepage: "https://github.com/chorenzo-dev/engine#readme", bugs: { url: "https://github.com/chorenzo-dev/engine/issues" }, files: [ "dist", "LICENSE", "README.md", "package.json" ], publishConfig: { access: "public", registry: "https://registry.npmjs.org/" }, engines: { node: ">=18.0.0" }, overrides: { "test-exclude": "^7.0.1", glob: "^10.4.1" }, devDependencies: { "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", "@eslint/js": "^9.31.0", "@jest/globals": "^30.0.4", "@semantic-release/exec": "^7.1.0", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^11.0.4", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/jest": "^30.0.0", "@types/node": "^24.0.10", "@types/react": "^19.1.8", "@types/write-file-atomic": "^4.0.3", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", "esbuild-plugin-copy": "^2.1.1", eslint: "^9.31.0", globals: "^16.3.0", husky: "^9.1.7", jest: "^30.0.4", "lint-staged": "^16.1.2", memfs: "^4.17.2", prettier: "^3.6.2", "semantic-release": "^24.2.7", "ts-jest": "^29.4.0", tsup: "^8.5.0", tsx: "^4.20.3", typescript: "^5.8.3" }, dependencies: { "@anthropic-ai/claude-code": "^1.0.38", commander: "^14.0.0", "fastest-levenshtein": "^1.0.16", handlebars: "^4.7.8", ink: "^6.0.1", "ink-select-input": "^6.2.0", "ink-spinner": "^5.0.0", "ink-text-input": "^6.0.0", pino: "^9.7.0", "pino-pretty": "^13.0.0", react: "^19.1.0", "simple-git": "^3.28.0", "write-file-atomic": "^6.0.0", yaml: "^2.8.0", zod: "^4.0.17" } }; // src/Shell.tsx import { Text as Text20 } from "ink"; import { useState as useState10 } from "react"; // src/containers/AnalysisValidateContainer.tsx import { useEffect } from "react"; // src/styles/icons.ts var icons = { success: "\u2705", error: "\u274C", warning: "\u26A0\uFE0F", celebration: "\u{1F389}", arrow: "\u2192", back: "\u2190", dash: "-", bullet: "\u2022" }; // src/utils/analyze.utils.ts import fs5 from "fs/promises"; import path4 from "path"; // src/schemas/analysis.schema.ts import { z } from "zod"; // src/types/analysis.ts var ProjectType = /* @__PURE__ */ ((ProjectType2) => { ProjectType2["CliTool"] = "cli_tool"; ProjectType2["WebApp"] = "web_app"; ProjectType2["ApiServer"] = "api_server"; ProjectType2["BackendService"] = "backend_service"; ProjectType2["Library"] = "library"; ProjectType2["Script"] = "script"; ProjectType2["Infrastructure"] = "infrastructure"; ProjectType2["DesktopApp"] = "desktop_app"; ProjectType2["MobileApp"] = "mobile_app"; ProjectType2["Unknown"] = "unknown"; return ProjectType2; })(ProjectType || {}); var CiCdSystem = /* @__PURE__ */ ((CiCdSystem2) => { CiCdSystem2["GithubActions"] = "github_actions"; CiCdSystem2["GitlabCi"] = "gitlab_ci"; CiCdSystem2["Circleci"] = "circleci"; CiCdSystem2["Jenkins"] = "jenkins"; CiCdSystem2["TravisCi"] = "travis_ci"; CiCdSystem2["AzureDevops"] = "azure_devops"; CiCdSystem2["BitbucketPipelines"] = "bitbucket_pipelines"; CiCdSystem2["Teamcity"] = "teamcity"; CiCdSystem2["Bamboo"] = "bamboo"; CiCdSystem2["Codeship"] = "codeship"; CiCdSystem2["Drone"] = "drone"; CiCdSystem2["Buildkite"] = "buildkite"; CiCdSystem2["Semaphore"] = "semaphore"; CiCdSystem2["Appveyor"] = "appveyor"; CiCdSystem2["None"] = "none"; return CiCdSystem2; })(CiCdSystem || {}); var Ecosystem = /* @__PURE__ */ ((Ecosystem2) => { Ecosystem2["Javascript"] = "javascript"; Ecosystem2["Python"] = "python"; Ecosystem2["Java"] = "java"; Ecosystem2["Dotnet"] = "dotnet"; Ecosystem2["Go"] = "go"; Ecosystem2["Rust"] = "rust"; Ecosystem2["Ruby"] = "ruby"; Ecosystem2["Php"] = "php"; Ecosystem2["Swift"] = "swift"; Ecosystem2["Dart"] = "dart"; Ecosystem2["Elixir"] = "elixir"; Ecosystem2["Haskell"] = "haskell"; Ecosystem2["Perl"] = "perl"; Ecosystem2["R"] = "r"; Ecosystem2["Julia"] = "julia"; Ecosystem2["Lua"] = "lua"; Ecosystem2["Unknown"] = "unknown"; return Ecosystem2; })(Ecosystem || {}); var Language = /* @__PURE__ */ ((Language2) => { Language2["JavaScript"] = "javascript"; Language2["TypeScript"] = "typescript"; Language2["Python"] = "python"; Language2["Java"] = "java"; Language2["Scala"] = "scala"; Language2["Kotlin"] = "kotlin"; Language2["Groovy"] = "groovy"; Language2["CSharp"] = "csharp"; Language2["FSharp"] = "fsharp"; Language2["VBNet"] = "vbnet"; Language2["Go"] = "go"; Language2["Rust"] = "rust"; Language2["Ruby"] = "ruby"; Language2["Php"] = "php"; Language2["Swift"] = "swift"; Language2["ObjectiveC"] = "objective-c"; Language2["Dart"] = "dart"; Language2["Elixir"] = "elixir"; Language2["Haskell"] = "haskell"; Language2["Perl"] = "perl"; Language2["R"] = "r"; Language2["Julia"] = "julia"; Language2["Lua"] = "lua"; Language2["C"] = "c"; Language2["Cpp"] = "cpp"; Language2["Unknown"] = "unknown"; return Language2; })(Language || {}); // src/schemas/analysis.schema.ts var ProjectTypeSchema = z.nativeEnum(ProjectType); var CiCdSystemSchema = z.nativeEnum(CiCdSystem); var EcosystemSchema = z.nativeEnum(Ecosystem); var LanguageSchema = z.nativeEnum(Language); var ProjectAnalysisSchema = z.object({ path: z.string().min(1, "Project path cannot be empty"), language: z.string().min(1, "Language cannot be empty"), type: ProjectTypeSchema, framework: z.string().nullable().optional(), dependencies: z.array(z.string()).default([]), hasPackageManager: z.boolean(), ecosystem: EcosystemSchema.optional(), dockerized: z.boolean().optional() }).strict(); var WorkspaceAnalysisSchema = z.object({ isMonorepo: z.boolean(), hasWorkspacePackageManager: z.boolean(), workspaceEcosystem: EcosystemSchema.optional(), workspaceDependencies: z.array(z.string()).optional().default([]), projects: z.array(ProjectAnalysisSchema).min(1, "At least one project must be present"), ciCd: CiCdSystemSchema.optional() }).strict(); // src/utils/json.utils.ts import * as fs4 from "fs"; var JsonError = class extends Error { constructor(message, code) { super(message); this.code = code; this.name = "JsonError"; } }; function readJson(filePath) { try { const fileContent = fs4.readFileSync(filePath, "utf8"); const data = JSON.parse(fileContent); return data; } catch (error) { if (error instanceof Error && "code" in error && error.code === "ENOENT") { throw new JsonError(`File not found: ${filePath}`, "FILE_NOT_FOUND"); } if (error instanceof SyntaxError) { throw new JsonError( `Invalid JSON in file ${filePath}: ${error.message}`, "PARSE_ERROR" ); } throw new JsonError( formatErrorMessage("Failed to read JSON file", error), "READ_ERROR" ); } } function writeJson(filePath, data, pretty = true) { try { const jsonContent = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data); fs4.writeFileSync(filePath, jsonContent, "utf8"); } catch (error) { throw new JsonError( formatErrorMessage("Failed to write JSON file", error), "WRITE_ERROR" ); } } // src/utils/analyze.utils.ts async function validateAnalysisFile(filePath) { try { const resolvedPath = path4.resolve(filePath); try { await fs5.access(resolvedPath); } catch { return { valid: false, errors: [ { path: "file", message: `Analysis file not found: ${filePath}`, code: "FILE_NOT_FOUND" } ] }; } let data; try { data = await readJson(resolvedPath); } catch (error) { if (error instanceof JsonError) { return { valid: false, errors: [ { path: "file", message: `Invalid JSON: ${error.message}`, code: "INVALID_JSON" } ] }; } throw error; } return validateAnalysisData(data); } catch (error) { return { valid: false, errors: [ { path: "file", message: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`, code: "UNEXPECTED_ERROR" } ] }; } } function validateAnalysisData(data) { try { const result = WorkspaceAnalysisSchema.safeParse(data); if (result.success) { return { valid: true, errors: [], data: result.data }; } const errors = result.error.issues.map( (issue) => { let message = issue.message; if (issue.code === "invalid_type" && issue.message.includes("received undefined")) { message = "is missing"; } else if (issue.code === "unrecognized_keys") { const keys = issue.keys || []; message = `unexpected field${keys.length > 1 ? "s" : ""}: ${keys.join(", ")}`; } return { path: issue.path.join(".") || "root", message, code: issue.code }; } ); return { valid: false, errors }; } catch (error) { return { valid: false, errors: [ { path: "root", message: `Validation error: ${error instanceof Error ? error.message : String(error)}`, code: "VALIDATION_ERROR" } ] }; } } function formatValidationErrors(errors) { if (errors.length === 0) { return "No validation errors"; } const missingFields = []; const invalidValues = []; const unexpectedFields = []; errors.forEach((error) => { if (error.message === "is missing") { missingFields.push(error.path); } else if (error.message.startsWith("unexpected field")) { const keys = error.message.match(/unexpected field[s]?: (.+)/)?.[1] || ""; keys.split(", ").forEach((key) => { const fullPath = error.path === "root" ? key : `${error.path}.${key}`; unexpectedFields.push(fullPath); }); } else { invalidValues.push(`${error.path}: ${error.message}`); } }); const lines = [ `Found ${errors.length} validation error${errors.length === 1 ? "" : "s"}:` ]; let errorIndex = 1; if (missingFields.length > 0) { lines.push( ` ${errorIndex++}. Missing fields: ${missingFields.join(", ")}` ); } if (invalidValues.length > 0) { invalidValues.forEach((error) => { lines.push(` ${errorIndex++}. Invalid field value: ${error}`); }); } if (unexpectedFields.length > 0) { lines.push( ` ${errorIndex++}. Unexpected fields: ${unexpectedFields.join(", ")}` ); } return lines.join("\n"); } function getDefaultAnalysisPath() { return workspaceConfig.getAnalysisPath(); } // src/commands/analysis.validate.ts async function analysisValidate(options, onProgress) { const filePath = options.file || getDefaultAnalysisPath(); Logger.info(`Starting validation process for: ${filePath}`); onProgress?.(`Checking if analysis file exists`); if (options.debug) { onProgress?.(`Looking for analysis file at: ${filePath}`); } onProgress?.(`Reading analysis file`); if (options.debug) { onProgress?.(`Reading and parsing JSON from: ${filePath}`); } onProgress?.(`Validating file structure`); if (options.debug) { onProgress?.(`Running Zod schema validation`); } const result = await validateAnalysisFile(filePath); if (result.valid) { onProgress?.(`${icons.success} Analysis file is valid`); Logger.info("Schema validation passed successfully"); if (options.debug) { onProgress?.( `Validated ${Object.keys(result.data || {}).length} top-level fields` ); if (result.data && typeof result.data === "object" && "projects" in result.data) { const projects = result.data.projects; if (Array.isArray(projects)) { onProgress?.( `Found ${projects.length} project${projects.length === 1 ? "" : "s"} in analysis` ); } } } } else { onProgress?.(`${icons.error} Validation failed`); const errorMessage = formatValidationErrors(result.errors); onProgress?.(errorMessage); Logger.error( `Schema validation failed with ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}` ); if (options.debug) { onProgress?.(`Detailed error breakdown:`); result.errors.forEach((error, index) => { onProgress?.( `Error ${index + 1}: ${error.path} - ${error.message} (${error.code})` ); }); onProgress?.(`Validation process completed with errors`); } throw new Error(errorMessage); } } // src/components/StepSequence.tsx import { Box as Box3 } from "ink"; import { useState as useState2 } from "react"; // src/contexts/ProgressContext.tsx import { createContext, useContext } from "react"; var ProgressContext = createContext(null); // src/hooks/useDebugMessages.tsx import { useState } from "react"; var useDebugMessages = (debugMode) => { const [debugMessages, setDebugMessages] = useState([]); const addDebugMessage = (stepId, type, message, isThinking) => { if (debugMode) { const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString(); setDebugMessages((prev) => { const lastMessage = prev[prev.length - 1]; if (lastMessage && lastMessage.message === message && lastMessage.type === type && lastMessage.stepId === stepId) { return prev; } return [...prev, { timestamp, type, message, stepId, isThinking }]; }); } }; return { debugMessages, addDebugMessage }; }; // src/components/StepSequence/CompletionRenderer.tsx import { Box as Box2 } from "ink"; // src/components/StepSequence/DebugMessagesList.tsx import { Text } from "ink"; // src/styles/colors.ts var colors = { success: "green", error: "red", warning: "yellow", info: void 0, progress: "cyan", muted: "gray", secondary: "magenta", default: void 0 }; // src/components/StepSequence/DebugMessagesList.tsx import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var DebugMessagesList = ({ messages }) => { return /* @__PURE__ */ jsx(Fragment, { children: messages.map((msg, i) => /* @__PURE__ */ jsxs(Text, { children: [ /* @__PURE__ */ jsxs(Text, { color: colors.muted, children: [ "[", msg.timestamp, "]" ] }), " ", msg.type === "complete" ? /* @__PURE__ */ jsx(Text, { children: msg.message }) : /* @__PURE__ */ jsx( Text, { color: msg.type === "processing" ? colors.info : colors.progress, children: msg.message } ) ] }, i)) }); }; // src/components/StepSequence/StepDisplay.tsx import { Box, Text as Text2 } from "ink"; import Spinner from "ink-spinner"; import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime"; var StepDisplay = ({ title, status, activity, error, isProcessing, isActivityProcessing, isTitleVisible = true, children }) => { const getIcon = () => { switch (status) { case "completed": return icons.success; case "error": return icons.error; default: return ""; } }; const getColor = () => { switch (status) { case "completed": return colors.success; case "error": return colors.error; case "in_progress": return colors.info; default: return colors.warning; } }; const icon = getIcon(); const color = getColor(); const displayTitle = title; return /* @__PURE__ */ jsxs2(Box, { flexDirection: "column", children: [ isTitleVisible && /* @__PURE__ */ jsx2(Box, { children: /* @__PURE__ */ jsxs2(Text2, { color, children: [ status === "in_progress" ? isProcessing ? /* @__PURE__ */ jsxs2(Fragment2, { children: [ /* @__PURE__ */ jsx2(Spinner, { type: "dots" }), " " ] }) : " " : icon ? `${icon} ` : "", displayTitle ] }) }), status === "in_progress" && activity && /* @__PURE__ */ jsx2(Box, { children: /* @__PURE__ */ jsxs2(Text2, { color: colors.progress, children: [ isActivityProcessing ? /* @__PURE__ */ jsxs2(Fragment2, { children: [ /* @__PURE__ */ jsx2(Spinner, { type: "dots" }), " " ] }) : " ", activity ] }) }), status === "error" && error && /* @__PURE__ */ jsx2(Box, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { color: colors.error, children: error }) }), children && /* @__PURE__ */ jsx2(Box, { marginLeft: status === "in_progress" ? 3 : 0, children }) ] }); }; // src/components/StepSequence/CompletionRenderer.tsx import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime"; var CompletionRenderer = ({ debugMode, debugMessages, completionTitle, completionComponent, stepContext }) => { if (debugMode) { return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", children: [ /* @__PURE__ */ jsx3(DebugMessagesList, { messages: debugMessages }), completionComponent ? /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: completionComponent(stepContext) || /* @__PURE__ */ jsx3(StepDisplay, { title: completionTitle, status: "completed" }) }) : /* @__PURE__ */ jsx3(StepDisplay, { title: completionTitle, status: "completed" }) ] }); } if (completionComponent) { const component = completionComponent(stepContext); if (component) { return /* @__PURE__ */ jsx3(Fragment3, { children: component }); } } return /* @__PURE__ */ jsx3(StepDisplay, { title: completionTitle, status: "completed" }); }; // src/components/StepSequence/ErrorRenderer.tsx import { Text as Text3 } from "ink"; import { jsx as jsx4 } from "react/jsx-runtime"; var ErrorRenderer = ({ error, errorTitle, errorComponent }) => { return errorComponent ? errorComponent(error) : /* @__PURE__ */ jsx4(StepDisplay, { title: errorTitle, status: "error", children: /* @__PURE__ */ jsx4(Text3, { color: "red", children: error.message }) }); }; // src/components/StepSequence.tsx import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime"; function getStepStatus(stepId, stepIndex, currentStepIndex, completedSteps, errorSteps) { if (errorSteps.has(stepId)) { return "error"; } if (completedSteps.has(stepId)) { return "completed"; } if (stepIndex === currentStepIndex) { return "in_progress"; } return "pending"; } var StepRenderer = ({ step, context }) => { return step.component(context); }; var StepSequence = ({ steps, completionTitle = "Process completed!", completionComponent, errorTitle = "Process failed!", errorComponent, onComplete, options = {}, debugMode = false }) => { const [currentStepIndex, setCurrentStepIndex] = useState2(0); const [completedSteps, setCompletedSteps] = useState2(/* @__PURE__ */ new Set()); const [errorSteps, setErrorSteps] = useState2(/* @__PURE__ */ new Set()); const [currentActivity, setCurrentActivity] = useState2(""); const [currentError, setCurrentError] = useState2(""); const [isStepProcessing, setIsStepProcessing] = useState2(false); const [isActivityProcessing, setIsActivityProcessing] = useState2(false); const [isTitleVisible, setIsTitleVisible] = useState2(true); const [results, setResults] = useState2({}); const { debugMessages, addDebugMessage } = useDebugMessages(debugMode); const isFlowComplete = completedSteps.size === steps.length; const flowError = errorSteps.size > 0 ? new Error(currentError) : null; const currentStep = steps[currentStepIndex]; const progressControls = { setActivity: (activity, processing = false) => { Logger.info( { event: "setActivity_debug", activity, processing }, `setActivity called: ${activity} (processing: ${processing})` ); if (debugMode && currentStep) { addDebugMessage(currentStep.id, "activity", activity, processing); } else { setCurrentActivity(activity); setIsActivityProcessing(processing); } }, setError: (error) => { if (debugMode && currentStep) { addDebugMessage(currentStep.id, "error", error); } setCurrentError(error); if (currentStep) { setErrorSteps((prev) => new Set(prev).add(currentStep.id)); } }, complete: () => { if (debugMode && currentStep) { addDebugMessage( currentStep.id, "complete", `${currentStep.title} - completed` ); } if (currentStep) { setCompletedSteps((prev) => new Set(prev).add(currentStep.id)); } setCurrentActivity(""); setCurrentError(""); setIsStepProcessing(false); setIsActivityProcessing(false); setIsTitleVisible(true); if (currentStepIndex < steps.length - 1) { const nextIndex = currentStepIndex + 1; setCurrentStepIndex(nextIndex); } else { onComplete?.(); } }, setProcessing: (processing) => { if (debugMode && currentStep && processing) { addDebugMessage( currentStep.id, "processing", `${currentStep.title} - started` ); } setIsStepProcessing(processing); }, setTitleVisible: (visible) => { setIsTitleVisible(visible); } }; const stepContext = { ...progressControls, setResult: (result) => { if (currentStep) { setResults((prev) => ({ ...prev, [currentStep.id]: result })); } }, getResult: (stepId) => results[stepId], options }; if (flowError) { return /* @__PURE__ */ jsx5( ErrorRenderer, { error: flowError, errorTitle, errorComponent } ); } if (isFlowComplete) { return /* @__PURE__ */ jsx5( CompletionRenderer, { debugMode, debugMessages, completionTitle, completionComponent, stepContext } ); } return /* @__PURE__ */ jsx5(ProgressContext.Provider, { value: progressControls, children: /* @__PURE__ */ jsx5(Box3, { flexDirection: "column", children: steps.map((step, index) => { const status = getStepStatus( step.id, index, currentStepIndex, completedSteps, errorSteps ); const isCurrentStep = index === currentStepIndex; const hasError = errorSteps.has(step.id); if (status === "pending") { return null; } if (debugMode) { if (isCurrentStep) { return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", children: [ /* @__PURE__ */ jsx5(DebugMessagesList, { messages: debugMessages }), /* @__PURE__ */ jsx5(StepRenderer, { step, context: stepContext }) ] }, step.id); } return null; } return /* @__PURE__ */ jsx5( StepDisplay, { title: step.title, status, activity: isCurrentStep ? currentActivity : void 0, error: hasError ? currentError : void 0, isProcessing: isCurrentStep ? isStepProcessing : void 0, isActivityProcessing: isCurrentStep ? isActivityProcessing : void 0, isTitleVisible: isCurrentStep ? isTitleVisible : true, children: isCurrentStep && /* @__PURE__ */ jsx5(StepRenderer, { step, context: stepContext }) }, step.id ); }) }) }); }; // src/containers/AnalysisValidateContainer.tsx import { jsx as jsx6 } from "react/jsx-runtime"; var AnalysisValidateContainer = ({ options, onError }) => { const steps = [ { id: "validation", title: "Validating analysis file", component: (context) => { useEffect(() => { const runValidation = async () => { context.setProcessing(true); try { await analysisValidate(options, (message) => { context.setActivity(message); }); context.complete(); } catch (error) { const errorMessage = extractErrorMessage(error); context.setError(errorMessage); process.exitCode = 1; onError(error instanceof Error ? error : new Error(errorMessage)); } }; runValidation(); }, [context]); return null; } } ]; return /* @__PURE__ */ jsx6(StepSequence, { steps, debugMode: options.debug }); }; // src/containers/AnalyzeContainer.tsx import { useEffect as useEffect2 } from "react"; // src/commands/analyze.ts import { query } from "@anthropic-ai/claude-code"; import * as fs9 from "fs"; import * as path8 from "path"; // src/utils/code-changes-events.utils.ts import * as os from "os"; async function executeCodeChangesOperation(operationPromise, handlers, startTime = /* @__PURE__ */ new Date()) { let sdkResultMetadata = null; let result = null; let errorMessage; let success = false; handlers.onThinkingStateChange?.(true); try { for await (const message of operationPromise) { Logger.debug( { event: "claude_message_received", messageType: message.type, messageSubtype: message["subtype"] || "none", hasContent: "content" in message, hasMessage: "message" in message }, `Claude message: ${message.type}` ); if (message.type === "result") { handlers.onThinkingStateChange?.(false); sdkResultMetadata = message; if (message.subtype === "success" && "result" in message) { result = message.result; success = true; Logger.info( { event: "claude_execution_success", resultLength: message.result ? String(message.result).length : 0 }, `Claude execution completed successfully. Result preview: ${String(message.result || "").substring(0, 200)}...` ); } else if (message.subtype?.startsWith("error")) { const messageObj = message; if ("error" in messageObj) { errorMessage = String(messageObj["error"]); } else if ("message" in messageObj) { errorMessage = String(messageObj["message"]); } else if ("reason" in messageObj) { errorMessage = String(messageObj["reason"]); } else { if ("content" in messageObj && messageObj["content"]) { const content = messageObj["content"]; errorMessage = content.length > 500 ? content.substring(0, 500) + "..." : content; } else { errorMessage = `Claude execution failed with ${message.subtype}`; } } Logger.error( { event: "claude_execution_error_detailed", subtype: message.subtype, errorMessage, fullMessage: JSON.stringify(messageObj, null, 2).substring( 0, 1e3 ) }, `Claude execution error: ${errorMessage}` ); success = false; } break; } else { if (message.type === "assistant" && "message" in message) { const assistantMessage = message.message; if (assistantMessage.content) { for (const content of assistantMessage.content) { if (content.type === "tool_use") { Logger.info( { event: "claude_tool_use", toolName: content["name"], hasInput: !!content["input"] }, `Claude tool use: ${content["name"]}` ); if (content["name"] === "Bash" && content["input"] && typeof content["input"] === "object") { const bashInput = content["input"]; if (bashInput.command) { Logger.info( { event: "claude_bash_command", command: bashInput.command.substring(0, 200) }, `Claude bash: ${bashInput.command}` ); } } const toolMessage = formatToolMessage( String(content["name"]), content["input"], handlers.showChorenzoOperations ); if (toolMessage) { handlers.onThinkingStateChange?.(false); handlers.onProgress?.(toolMessage); } } else if (content.type === "text") { Logger.debug( { event: "claude_text_response", textLength: content.text ? content.text.length : 0 }, `Claude text: ${content.text ? content.text.substring(0, 200) : ""}...` ); } } } } else if (message.type === "user" && "message" in message) { const userMessage = message.message; if (userMessage.content) { for (const content of userMessage.content) { if (content.type === "tool_result") { Logger.debug( { event: "claude_tool_result", toolUseId: content.tool_use_id || "unknown", isError: content.is_error || false, contentLength: content["content"] ? String(content["content"]).length : 0 }, `Tool result: ${content.is_error ? "error" : "success"}` ); } } } handlers.onThinkingStateChange?.(true); } } } const endTime = /* @__PURE__ */ new Date(); const durationSeconds = (endTime.getTime() - startTime.getTime()) / 1e3; let totalCost = 0; let totalTurns = 0; let subtype = "error"; if (sdkResultMetadata?.type === "result") { if ("total_cost_usd" in sdkResultMetadata) { totalCost = sdkResultMetadata.total_cost_usd; } if ("num_turns" in sdkResultMetadata) { totalTurns = sdkResultMetadata.num_turns; } if ("subtype" in sdkResultMetadata) { subtype = sdkResultMetadata.subtype; } } const metadata = { costUsd: totalCost, turns: totalTurns, durationSeconds, subtype: success ? subtype : "error" }; if (success && result !== null) { handlers.onComplete?.(result, metadata); return { success: true, result, metadata }; } else { const finalErrorMessage = errorMessage || "Claude operation failed without specific error details"; Logger.error( { event: "claude_operation_failed", hasErrorMessage: !!errorMessage, errorMessage: finalErrorMessage, totalCost, totalTurns, durationSeconds }, `Claude operation failed: ${finalErrorMessage}` ); const error = new Error(finalErrorMessage); handlers.onError?.(error); return { success: false, error: formatErrorMessage("Claude operation failed", error), metadata }; } } catch (error) { const endTime = /* @__PURE__ */ new Date(); const durationSeconds = (endTime.getTime() - startTime.getTime()) / 1e3; const errorMessage2 = extractErrorMessage(error); Logger.error( { event: "code_changes_operation_exception", errorType: error instanceof Error ? error.constructor.name : typeof error, errorMessage: errorMessage2, stack: error instanceof Error ? error.stack : void 0, durationSeconds }, `Code changes operation threw exception: ${errorMessage2}` ); const metadata = { costUsd: 0, turns: 0, durationSeconds, subtype: "error" }; const finalError = error instanceof Error ? error : new Error(errorMessage2); handlers.onError?.(finalError); return { success: false, error: formatErrorMessage("Code changes operation failed", error), metadata }; } } function formatToolMessage(toolName, input, showChorenzoOperations) { if (toolName === "TodoWrite" || toolName === "TodoRead") { return null; } if (!input || typeof input !== "object") { return `Using ${toolName} tool`; } switch (toolName) { case "Read": { const fileInput = input; const readPath = getRelativePath(fileInput.file_path) || "file"; if (isChorenzoPath(readPath) && !showChorenzoOperations) { return "Updating Chorenzo context"; } return `Reading ${readPath}`; } case "Write": { const fileInput = input; const writePath = getRelativePath(fileInput.file_path) || "file"; if (isChorenzoPath(writePath) && !showChorenzoOperations) { return "Updating Chorenzo context"; } return `Writing ${writePath}`; } case "Edit": case "MultiEdit": { const fileInput = input; const editPath = getRelativePath(fileInput.file_path) || "file"; if (isChorenzoPath(editPath) && !showChorenzoOperations) { return "Updating Chorenzo context"; } return `Editing ${editPath}`; } case "Bash": { const bashInput = input; const command = bashInput.command || bashInput.cmd || ""; if (command.includes("mkdir") && command.includes(".chorenzo")) { return "Initializing the chorenzo engine"; } if (command.includes("mkdir")) { const pathMatch = command.match(/mkdir\s+(-p\s+)?["']?([^"'\s]+)["']?/); if (pathMatch?.[2]) { const relativePath = getRelativePath(pathMatch[2]); return `Creating directory: ${relativePath}`; } } if (command.includes("rm ") || command.includes("rmdir")) { const pathMatch = command.match( /(?:rm|rmdir)\s+(-[rf]+\s+)?["']?([^"'\s]+)["']?/ ); if (pathMatch?.[2]) { const relativePath = getRelativePath(pathMatch[2]); return `Removing: ${relativePath}`; } } return `Running: ${command}`; } case "LS": { const relativePath = getRelativePath(input.path); let pathDisplay; if (relativePath === "") { pathDisplay = "root directory"; } else if (relativePath) { pathDisplay = relativePath; } else { pathDisplay = input.path || "directory"; } return `Listing ${pathDisplay}`; } case "Glob": return `Finding files: ${input.pattern || "pattern"}`; case "Grep": return `Searching for: ${input.pattern || "pattern"}`; case "Task": return `Running task: ${input.description || "background task"}`; default: return `Using ${toolName} tool`; } } function getRelativePath(filePath) { if (!filePath) { return filePath; } const workspaceRoot = workspaceConfig.getWorkspaceRoot(); if (filePath.startsWith(workspaceRoot)) { const relativePath = filePath.substring(workspaceRoot.length); return relativePath.startsWith("/") ? relativePath.substring(1) : relativePath; } const homeDir = os.homedir(); if (filePath.startsWith(homeDir)) { const relativePath = filePath.substring(homeDir.length); return `~${relativePath}`; } return filePath; } function isChorenzoPath(filePath) { if (!filePath) { return false; } return filePath.includes(".chorenzo"); } // src/utils/file-tree.utils.ts import fs7 from "fs/promises"; import path6 from "path"; // src/utils/gitignore.utils.ts import * as fs6 from "fs"; import * as path5 from "path"; var GitignoreManager = class { static CHORENZO_SECTION_START = "# Chorenzo"; static CHORENZO_PATTERNS = [ "/.chorenzo/*", "!/.chorenzo/state.json", "!/.chorenzo/analysis.json" ]; static ensureChorenzoIgnorePatterns(projectRoot) { const gitignorePath = path5.join(projectRoot, ".gitignore"); try { if (!fs6.existsSync(gitignorePath)) { this.createGitignoreWithChorenzoPatterns(gitignorePath); return; } const content = fs6.readFileSync(gitignorePath, "utf-8"); const updatedContent = this.updateChorenzoPatterns(content); if (updatedContent !== content) { fs6.writeFileSync(gitignorePath, updatedContent, "utf-8"); Logger.info( { event: "gitignore_updated", path: gitignorePath }, "Updated .gitignore with Chorenzo state file tracking" ); } } catch (error) { Logger.warn( { event: "gitignore_update_failed", path: gitignorePath, error: formatErrorMessage("gitignore update", error) }, "Failed to update .gitignore for Chorenzo state files" ); } } static createGitignoreWithChorenzoPatterns(gitignorePath) { const content = this.generateChorenzoSection(); fs6.writeFileSync(gitignorePath, content, "utf-8"); Logger.info( { event: "gitignore_created", path: gitignorePath }, "Created .gitignore with Chorenzo state file tracking" ); } static updateChorenzoPatterns(content) { const hasChorenzoSection = content.includes(this.CHORENZO_SECTION_START); if (hasChorenzoSection) { return this.replaceChorenzoSection(content); } return this.appendChorenzoSection(content); } static replaceChorenzoSection(content) { const startIndex = content.indexOf(this.CHORENZO_SECTION_START); if (startIndex === -1) { return this.appendChorenzoSection(content); } const lines = content.split("\n"); const startLineIndex = lines.findIndex( (line) => line.includes(this.CHORENZO_SECTION_START) ); if (startLineIndex === -1) { return this.appendChorenzoSection(content); } let endLineIndex = startLineIndex + 1; while (endLineIndex < lines.length) { const line = lines[endLineIndex]?.trim(); if (!line || !line.startsWith("/.chorenzo") && !line.startsWith("!/.chorenzo")) { break; } endLineIndex++; } const before = lines.slice(0, startLineIndex).join("\n"); const after = lines.slice(endLineIndex).join("\n"); const newSection = this.generateChorenzoSection(); const beforeTrimmed = before.trim(); const afterTrimmed = after.trim(); const separator = beforeTrimmed && afterTrimmed ? "\n\n" : beforeTrimmed ? "\n" : ""; return beforeTrimmed + (beforeTrimmed ? "\n\n" : "") + newSection + separator + afterTrimmed; } static appendChorenzoSection(content) { const trimmedContent = content.trim(); const separator = trimmedContent ? "\n\n" : ""; return trimmedContent + separator + this.generateChorenzoSection(); } static generateChorenzoSection() { const patterns = this.CHORENZO_PATTERNS.join("\n"); return `${this.CHORENZO_SECTION_START} ${patterns} `; } static loadGitIgnorePatternsForDir(directory, parentPatterns = /* @__PURE__ */ new Set()) { const patterns = new Set(parentPatterns); const gitignorePath = path5.join(directory, ".gitignore"); if (fs6.existsSync(gitignorePath)) { try { const content = fs6.readFileSync(gitignorePath, "utf-8"); const lines = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")); for (const line of lines) { patterns.add(line); } } catch (error) { Logger.warn( { event: "gitignore_read_failed", path: gitignorePath, error: formatErrorMessage("gitignore read", error) }, "Failed to read .gitignore file" ); } } return patterns; } static isIgnored(filePath, rootDir, ignorePatterns) { const relPath = path5.relative(rootDir, filePath); const parts = relPath.split(path5.sep); for (const pattern of ignorePatterns) { if (this.matchGitIgnorePattern(relPath, pattern) || relPath.startsWith(pattern) || parts.includes(pattern)) { return true; } } return false; } static matchGitIgnorePattern(filePath, pattern) { const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\*\*/g, ".*"); const regex = new RegExp(`^${regexPattern}$`); return regex.test(filePath) || regex.test(path5.basename(filePath)); } }; // src/utils/file-tree.utils.ts var lockFiles = /* @__PURE__ */ new Set([ "package-lock.json", "uv.lock", "poetry.lock", "Pipfile.lock", "yarn.lock", "pnpm-lock.yaml", "shrinkwrap.yaml" ]); async function buildFileTree(rootDir, subdir, numLevels) { const seen = /* @__PURE__ */ new Set(); async function buildTree(currentPath, parentPatterns, level = 0) { if (numLevels !== void 0 && level >= numLevels) { return null; } const absPath = path6.resolve(currentPath); if (seen.has(absPath)) { return null; } seen.add(absPath); const patterns = GitignoreManager.loadGitIgnorePatternsForDir( currentPath, parentPatterns ); if (GitignoreManager.isIgnored(currentPath, rootDir, patterns)) { return null; } const stats = await fs7.stat(absPath); if (stats.isDirectory()) { const baseName = path6.basename(absPath); if (baseName === ".git") { return null; } const children = {}; const files = []; const entries = await fs7.readdir(absPath); const sortedEntries = entries.sort( (a, b) => a.toLowerCase().localeCompare(b.toLowerCase()) ); for (const entry of sortedEntries) { if (lockFiles.has(entry)) { continue; } const childPath = path6.join(absPath, entry); const childStats = await fs7.stat(childPath); if (childStats.isDirectory()) { const subtree = await buildTree(childPath, patterns, level + 1); if (subtree !== null) { children[entry] = subtree; } } else if (childStats.isFile()) { if (!GitignoreManager.isIgnored(childPath, rootDir, patterns)) { files.push(entry); } } } if (Object.keys(children).length > 0 && files.length > 0) { return { ...children, __files__: files }; } else if (Object.keys(children).length > 0) { return children; } else if (files.length > 0) { return files; } else { return null; } } return null; } const tree = await buildTree(rootDir, /* @__PURE__ */ new Set(), 0); function toYamlStyle(name, node, indent = 0) { const pad = " ".repeat(indent); if (node === null) { return [`${pad}${name}: (empty)`]; } if (Array.isArray(node)) { const lines2 = [`${pad}${name}:`]; for (const file of node) { lines2.push(`${pad} - ${file}`); } return lines2; } const lines = [`${pad}${name}:`]; for (const [key, value] of Object.entries(node)) { if (key === "__files__" && Array.isArray(value)) { for (const file of value) { lin