UNPKG

codestate-cli

Version:

CodeState CLI - Configuration, Script, and Git Management

1,418 lines (1,376 loc) 55.5 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 __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 )); // packages/cli-interface/cli.ts var import_core27 = require("codestate-core"); // packages/cli-interface/commands/index.ts var import_core26 = require("codestate-core"); // packages/cli-interface/commands/config/showConfig.ts var import_core = require("codestate-core"); async function showConfigCommand() { const logger2 = new import_core.ConfigurableLogger(); const getConfig = new import_core.GetConfig(); const result = await getConfig.execute(); if (result.ok) { const config = result.value; logger2.plainLog("\n\u{1F4CB} Current Configuration:"); logger2.plainLog("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); logger2.plainLog(`Editor: ${config.ide}`); logger2.plainLog(`Version: ${config.version}`); logger2.plainLog(`Encryption: ${config.encryption.enabled ? "Yes" : "No"}`); logger2.plainLog(`Storage Path: ${config.storagePath}`); logger2.plainLog(`Log Level: ${config.logger.level}`); logger2.plainLog(`Log Sinks: ${config.logger.sinks.join(", ")}`); if (config.experimental && Object.keys(config.experimental).length > 0) { logger2.plainLog("\n\u{1F52C} Experimental Features:"); Object.entries(config.experimental).forEach(([key, value]) => { logger2.plainLog(` ${key}: ${value ? "\u2705" : "\u274C"}`); }); } if (config.extensions && Object.keys(config.extensions).length > 0) { logger2.plainLog("\n\u{1F50C} Extensions:"); Object.keys(config.extensions).forEach((key) => { logger2.plainLog(` ${key}`); }); } logger2.plainLog(""); } else { logger2.error("Failed to load config", { error: result.error }); } } // packages/cli-interface/tui/config/showConfigTui.ts async function showConfigTui() { await showConfigCommand(); } // packages/cli-interface/utils/inquirer.ts var import_core2 = require("codestate-core"); var import_inquirer = __toESM(require("inquirer")); var inquirer = { ...import_inquirer.default, customPrompt: async function(questions) { try { return await import_inquirer.default.prompt(questions); } catch (error) { if (error.message?.includes("SIGINT") || error.message?.includes("force closed")) { const logger2 = new import_core2.ConfigurableLogger(); logger2.plainLog("\n\u{1F44B} You have exited CodeState CLI"); process.exit(0); } throw error; } } }; var inquirer_default = inquirer; // packages/cli-interface/commands/config/updateConfig.ts var import_core3 = require("codestate-core"); async function updateConfigCommand(partial) { const logger2 = new import_core3.ConfigurableLogger(); const updateConfig = new import_core3.UpdateConfig(); const result = await updateConfig.execute(partial); if (result.ok) { const config = result.value; logger2.log("Configuration updated successfully!"); logger2.plainLog("\n\u{1F4CB} Current Configuration:"); logger2.plainLog("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); logger2.plainLog(`Editor: ${config.ide}`); logger2.plainLog(`Version: ${config.version}`); logger2.plainLog(`Encryption: ${config.encryption.enabled ? "Yes" : "No"}`); logger2.plainLog(`Storage Path: ${config.storagePath}`); logger2.plainLog(`Log Level: ${config.logger.level}`); logger2.plainLog(`Log Sinks: ${config.logger.sinks.join(", ")}`); if (config.experimental && Object.keys(config.experimental).length > 0) { logger2.plainLog("\n\u{1F52C} Experimental Features:"); Object.entries(config.experimental).forEach(([key, value]) => { logger2.plainLog(` ${key}: ${value ? "\u2705" : "\u274C"}`); }); } if (config.extensions && Object.keys(config.extensions).length > 0) { logger2.plainLog("\n\u{1F50C} Extensions:"); Object.keys(config.extensions).forEach((key) => { logger2.plainLog(` ${key}`); }); } logger2.plainLog(""); } else { logger2.error("Failed to update config", { error: result.error }); } } // packages/cli-interface/tui/config/updateConfigTui.ts async function updateConfigTui() { const answers = await inquirer_default.customPrompt([ { name: "ide", message: "Default IDE:", type: "list", choices: ["cursor", "vscode"] }, { name: "encryption", message: "Enable encryption?", type: "confirm" } ]); let encryptionKey = void 0; if (answers.encryption) { const keyAnswer = await inquirer_default.customPrompt([ { name: "encryptionKey", message: "Encryption key:", type: "password", mask: "*" } ]); encryptionKey = keyAnswer.encryptionKey; } const partial = { ide: answers.ide, encryption: { enabled: answers.encryption, encryptionKey } }; await updateConfigCommand(partial); } // packages/cli-interface/commands/config/resetConfig.ts var import_core4 = require("codestate-core"); async function resetConfigCommand() { const logger2 = new import_core4.ConfigurableLogger(); const resetConfig = new import_core4.ResetConfig(); const result = await resetConfig.execute(); if (result.ok) { logger2.log("Config reset to defaults:", { config: result.value }); } else { logger2.error("Failed to reset config", { error: result.error }); } } // packages/cli-interface/tui/config/resetConfigTui.ts async function resetConfigTui() { const { confirm } = await inquirer_default.customPrompt([ { name: "confirm", message: "Are you sure you want to reset config to defaults?", type: "confirm" } ]); if (confirm) { await resetConfigCommand(); } } // packages/cli-interface/tui/config/exportConfigTui.ts var import_core6 = require("codestate-core"); var fs = __toESM(require("fs/promises")); // packages/cli-interface/commands/config/exportConfig.ts var import_core5 = require("codestate-core"); async function exportConfigCommand() { const logger2 = new import_core5.ConfigurableLogger(); const exportConfig = new import_core5.ExportConfig(); const result = await exportConfig.execute(); if (result.ok) { logger2.log("Exported config:", { config: result.value }); } else { logger2.error("Failed to export config", { error: result.error }); } } // packages/cli-interface/tui/config/exportConfigTui.ts async function exportConfigTui() { const logger2 = new import_core6.ConfigurableLogger(); const { filePath } = await inquirer_default.customPrompt([ { name: "filePath", message: "Export to file (leave blank to print to console):", type: "input" } ]); let output = ""; const originalLog = logger2.log; logger2.log = (msg, meta) => { if (typeof msg === "string" && msg.startsWith("Exported config:")) { output = meta?.config || ""; } else { originalLog(msg, meta); } }; await exportConfigCommand(); logger2.log = originalLog; if (filePath && output) { await fs.writeFile(filePath, output, "utf8"); logger2.log(`Config exported to ${filePath}`); } else if (output) { logger2.plainLog(output); } } // packages/cli-interface/commands/config/importConfig.ts var import_core7 = require("codestate-core"); async function importConfigCommand(json) { const logger2 = new import_core7.ConfigurableLogger(); const importConfig = new import_core7.ImportConfig(); const result = await importConfig.execute(json); if (result.ok) { logger2.log("Config imported:", { config: result.value }); } else { logger2.error("Failed to import config", { error: result.error }); } } // packages/cli-interface/tui/config/importConfigTui.ts var fs2 = __toESM(require("fs/promises")); async function importConfigTui() { const { importType } = await inquirer_default.customPrompt([ { name: "importType", message: "Import from:", type: "list", choices: ["File", "Paste JSON"] } ]); let json = ""; if (importType === "File") { const { filePath } = await inquirer_default.customPrompt([ { name: "filePath", message: "Path to config file:", type: "input" } ]); json = await fs2.readFile(filePath, "utf8"); } else { const { jsonString } = await inquirer_default.customPrompt([ { name: "jsonString", message: "Paste config JSON:", type: "editor" } ]); json = jsonString; } await importConfigCommand(json); } // packages/cli-interface/tui/config/cliHandler.ts var import_core8 = require("codestate-core"); async function handleConfigCommand(subcommand, options) { const logger2 = new import_core8.ConfigurableLogger(); switch (subcommand) { case "show": await showConfigTui(); break; case "edit": await updateConfigTui(); break; case "reset": await resetConfigTui(); break; case "export": await exportConfigTui(); break; case "import": const fileIndex = options.indexOf("--file"); if (fileIndex === -1 || fileIndex === options.length - 1) { logger2.error("Error: --file option is required for import command"); logger2.plainLog("Usage: codestate config import --file <path>"); process.exit(1); } const filePath = options[fileIndex + 1]; await importConfigTui(); break; default: logger2.error(`Error: Unknown config subcommand '${subcommand}'`); logger2.plainLog( "Available config subcommands: show, edit, reset, export, import" ); process.exit(1); } } // packages/cli-interface/commands/scripts/showScripts.ts var import_core9 = require("codestate-core"); async function showScriptsCommand() { const logger2 = new import_core9.ConfigurableLogger(); const getScripts = new import_core9.GetScripts(); const result = await getScripts.execute(); if (result.ok) { const scripts = result.value; if (scripts.length === 0) { logger2.plainLog("\n\u{1F4DD} No scripts found."); logger2.plainLog( "Use `codestate scripts create` to add your first script.\n" ); return; } const scriptsByPath = /* @__PURE__ */ new Map(); scripts.forEach((script) => { if (!scriptsByPath.has(script.rootPath)) { scriptsByPath.set(script.rootPath, []); } scriptsByPath.get(script.rootPath).push(script); }); logger2.plainLog("\n\u{1F4DD} Scripts by Project Path:"); logger2.plainLog("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); scriptsByPath.forEach((pathScripts, rootPath) => { logger2.plainLog( ` \u{1F4C1} ${rootPath} (${pathScripts.length} script${pathScripts.length > 1 ? "s" : ""})` ); logger2.plainLog("\u2500".repeat(rootPath.length + 10)); pathScripts.forEach((script) => { logger2.plainLog(` \u2022 ${script.name} - ${script.script}`); }); }); logger2.plainLog(""); } else { logger2.error("Failed to load scripts", { error: result.error }); } } // packages/cli-interface/tui/scripts/showScriptsTui.ts async function showScriptsTui() { await showScriptsCommand(); } // packages/cli-interface/commands/scripts/createScript.ts var import_core10 = require("codestate-core"); async function createScriptCommand(scripts) { const logger2 = new import_core10.ConfigurableLogger(); const createScripts = new import_core10.CreateScripts(); const scriptsArray = Array.isArray(scripts) ? scripts : [scripts]; const result = await createScripts.execute(scriptsArray); if (result.ok) { const scriptNames = scriptsArray.map((s) => s.name).join(", "); if (scriptsArray.length === 1) { logger2.log(`Script '${scriptNames}' created successfully`); } else { logger2.log(`Scripts created successfully: ${scriptNames}`); } } else { logger2.error("Failed to create scripts", { error: result.error, count: scriptsArray.length }); } } // packages/cli-interface/tui/scripts/createScriptTui.ts async function createScriptTui() { await createScriptsInteractively(); } async function createScriptsInteractively() { const scripts = []; let continueAdding = true; const currentPath = process.cwd(); while (continueAdding) { const answers = await inquirer_default.customPrompt([ { name: "name", message: `Script name (${scripts.length + 1}):`, type: "input", validate: (input) => input.trim() ? true : "Script name is required" }, { name: "rootPath", message: `Root path (current: ${currentPath}):`, type: "input", default: currentPath, validate: (input) => input.trim() ? true : "Root path is required" }, { name: "script", message: "Script command:", type: "input", validate: (input) => input.trim() ? true : "Script command is required" }, { name: "addAnother", message: "Add another script?", type: "confirm", default: true } ]); scripts.push({ name: answers.name.trim(), rootPath: answers.rootPath.trim(), script: answers.script.trim() }); continueAdding = answers.addAnother; } if (scripts.length > 0) { await createScriptCommand(scripts); } } // packages/cli-interface/commands/scripts/updateScript.ts var import_core11 = require("codestate-core"); async function updateScriptCommand(name, rootPath, scriptUpdate) { const logger2 = new import_core11.ConfigurableLogger(); const updateScript = new import_core11.UpdateScript(); const result = await updateScript.execute(name, rootPath, scriptUpdate); if (result.ok) { const updatedFields = Object.keys(scriptUpdate).join(", "); logger2.log(`Script '${name}' updated successfully (${updatedFields})`); } else { logger2.error(`Failed to update script '${name}'`, { error: result.error }); } } // packages/cli-interface/tui/scripts/updateScriptTui.ts async function updateScriptTui() { const currentPath = process.cwd(); const answers = await inquirer_default.customPrompt([ { name: "name", message: "Script name to update:", type: "input", validate: (input) => input.trim() ? true : "Script name is required" }, { name: "rootPath", message: `Root path (current: ${currentPath}):`, type: "input", default: currentPath, validate: (input) => input.trim() ? true : "Root path is required" }, { name: "newName", message: "New script name (leave empty to keep current):", type: "input" }, { name: "newScript", message: "New script command (leave empty to keep current):", type: "input" } ]); const scriptUpdate = {}; if (answers.newName.trim()) { scriptUpdate.name = answers.newName.trim(); } if (answers.newScript.trim()) { scriptUpdate.script = answers.newScript.trim(); } await updateScriptCommand( answers.name.trim(), answers.rootPath.trim(), scriptUpdate ); } // packages/cli-interface/tui/scripts/deleteScriptTui.ts var import_core13 = require("codestate-core"); // packages/cli-interface/commands/scripts/deleteScript.ts var import_core12 = require("codestate-core"); async function deleteScriptCommand(name, rootPath) { const logger2 = new import_core12.ConfigurableLogger(); const deleteScript = new import_core12.DeleteScript(); const result = await deleteScript.execute(name, rootPath); if (result.ok) { logger2.log(`Script '${name}' deleted successfully`); } else { logger2.error(`Failed to delete script '${name}'`, { error: result.error }); } } // packages/cli-interface/tui/scripts/deleteScriptTui.ts async function deleteScriptTui() { const logger2 = new import_core13.ConfigurableLogger(); const currentPath = process.cwd(); const answers = await inquirer_default.customPrompt([ { name: "name", message: "Script name to delete:", type: "input", validate: (input) => input.trim() ? true : "Script name is required" }, { name: "rootPath", message: `Root path (current: ${currentPath}):`, type: "input", default: currentPath, validate: (input) => input.trim() ? true : "Root path is required" }, { name: "confirm", message: "Are you sure you want to delete this script?", type: "confirm", default: false } ]); if (answers.confirm) { await deleteScriptCommand(answers.name.trim(), answers.rootPath.trim()); } else { logger2.plainLog("Script deletion cancelled."); } } // packages/cli-interface/tui/scripts/deleteScriptsByRootPathTui.ts var import_core15 = require("codestate-core"); // packages/cli-interface/commands/scripts/deleteScriptsByRootPath.ts var import_core14 = require("codestate-core"); async function deleteScriptsByRootPathCommand(rootPath) { const logger2 = new import_core14.ConfigurableLogger(); const deleteScriptsByRootPath = new import_core14.DeleteScriptsByRootPath(); const result = await deleteScriptsByRootPath.execute(rootPath); if (result.ok) { logger2.log("Scripts deleted for root path successfully", { rootPath }); } else { logger2.error("Failed to delete scripts for root path", { error: result.error, rootPath }); } } // packages/cli-interface/tui/scripts/deleteScriptsByRootPathTui.ts async function deleteScriptsByRootPathTui() { const logger2 = new import_core15.ConfigurableLogger(); const currentPath = process.cwd(); const answers = await inquirer_default.customPrompt([ { name: "rootPath", message: `Root path to delete all scripts from (current: ${currentPath}):`, type: "input", default: currentPath, validate: (input) => input.trim() ? true : "Root path is required" }, { name: "confirm", message: "Are you sure you want to delete ALL scripts for this root path?", type: "confirm", default: false } ]); if (answers.confirm) { await deleteScriptsByRootPathCommand(answers.rootPath.trim()); } else { logger2.plainLog("Script deletion cancelled."); } } // packages/cli-interface/commands/scripts/exportScripts.ts var import_core16 = require("codestate-core"); async function exportScriptsCommand() { const logger2 = new import_core16.ConfigurableLogger(); const exportScripts = new import_core16.ExportScripts(); const result = await exportScripts.execute(); if (result.ok) { logger2.log("Scripts exported successfully:", { scripts: result.value }); } else { logger2.error("Failed to export scripts", { error: result.error }); } } // packages/cli-interface/tui/scripts/exportScriptsTui.ts async function exportScriptsTui() { await exportScriptsCommand(); } // packages/cli-interface/commands/scripts/importScripts.ts var import_core17 = require("codestate-core"); async function importScriptsCommand(json) { const logger2 = new import_core17.ConfigurableLogger(); const importScripts = new import_core17.ImportScripts(); const result = await importScripts.execute(json); if (result.ok) { logger2.log("Scripts imported successfully"); } else { logger2.error("Failed to import scripts", { error: result.error }); } } // packages/cli-interface/tui/scripts/importScriptsTui.ts var fs3 = __toESM(require("fs/promises")); async function importScriptsTui() { const { importType } = await inquirer_default.customPrompt([ { name: "importType", message: "Import from:", type: "list", choices: ["File", "Paste JSON"] } ]); let json = ""; if (importType === "File") { const { filePath } = await inquirer_default.customPrompt([ { name: "filePath", message: "Path to scripts file:", type: "input" } ]); json = await fs3.readFile(filePath, "utf8"); } else { const { jsonString } = await inquirer_default.customPrompt([ { name: "jsonString", message: "Paste scripts JSON:", type: "editor" } ]); json = jsonString; } await importScriptsCommand(json); } // packages/cli-interface/tui/scripts/cliHandler.ts var import_core19 = require("codestate-core"); // packages/cli-interface/commands/scripts/showScriptsByRootPath.ts var import_core18 = require("codestate-core"); async function showScriptsByRootPathCommand(rootPath) { const logger2 = new import_core18.ConfigurableLogger(); const getScriptsByRootPath = new import_core18.GetScriptsByRootPath(); const result = await getScriptsByRootPath.execute(rootPath); if (result.ok) { logger2.log(`Scripts for ${rootPath}:`, { scripts: result.value }); } else { logger2.error("Failed to load scripts for root path", { error: result.error, rootPath }); } } // packages/cli-interface/tui/scripts/cliHandler.ts async function handleScriptCommand(subcommand, options) { const logger2 = new import_core19.ConfigurableLogger(); switch (subcommand) { case "show": await showScriptsTui(); break; case "show-by-path": if (options.length === 0) { logger2.error("Error: root path is required for show-by-path command"); logger2.plainLog("Usage: codestate scripts show-by-path <root-path>"); process.exit(1); } await showScriptsByRootPathCommand(options[0]); break; case "create": await createScriptTui(); break; case "update": await updateScriptTui(); break; case "delete": await deleteScriptTui(); break; case "delete-by-path": await deleteScriptsByRootPathTui(); break; case "export": await exportScriptsTui(); break; case "import": await importScriptsTui(); break; default: logger2.error(`Error: Unknown scripts subcommand '${subcommand}'`); logger2.plainLog( "Available scripts subcommands: show, show-by-path, create, update, delete, delete-by-path, export, import" ); process.exit(1); } } // packages/cli-interface/tui/session/cliHandler.ts var import_core25 = require("codestate-core"); // packages/cli-interface/commands/session/saveSession.ts var import_core20 = require("codestate-core"); // packages/cli-interface/commands/session/utils.ts async function promptSessionDetails(defaults) { return inquirer_default.customPrompt([ { type: "input", name: "sessionName", message: "Enter session name:", default: defaults?.name || "", validate: (input) => { if (!input.trim()) { return "Session name is required"; } return true; } }, { type: "input", name: "sessionNotes", message: "Enter session notes (optional):", default: defaults?.notes || "" }, { type: "input", name: "sessionTags", message: "Enter session tags (comma-separated, optional):", default: defaults?.tags || "" } ]); } async function promptDirtyState(gitStatus, canStash) { const choices = [{ name: "Commit changes", value: "commit" }]; if (canStash) { choices.push({ name: "Stash changes", value: "stash" }); } choices.push({ name: "Cancel", value: "cancel" }); return inquirer_default.customPrompt([ { type: "list", name: "dirtyAction", message: "How would you like to handle these changes?", choices } ]); } async function getCurrentGitState(gitService, logger2) { const currentBranchResult = await gitService.getCurrentBranch(); const currentCommitResult = await gitService.getCurrentCommit(); const isDirtyResult = await gitService.getIsDirty(); if (!currentBranchResult.ok || !currentCommitResult.ok || !isDirtyResult.ok) { logger2.error("Failed to get Git state", { branchError: currentBranchResult.ok ? void 0 : currentBranchResult.error, commitError: currentCommitResult.ok ? void 0 : currentCommitResult.error, isDirtyError: isDirtyResult.ok ? void 0 : isDirtyResult.error }); return null; } return { branch: currentBranchResult.value, commit: currentCommitResult.value, isDirty: isDirtyResult.value, stashId: null // No stash ID for current state }; } async function handleSessionSave({ sessionDetails, projectRoot, git, saveSession, logger: logger2 }) { const result = await saveSession.execute({ name: sessionDetails.sessionName, projectRoot, notes: sessionDetails.sessionNotes || "", tags: sessionDetails.sessionTags.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0), files: [], git, extensions: {} }); if (result.ok) { logger2.log( `\u2705 Session "${sessionDetails.sessionName}" saved successfully!` ); } else { logger2.error("Failed to save session", { error: result.error }); } return result; } // packages/cli-interface/commands/session/saveSession.ts async function saveSessionCommand() { const logger2 = new import_core20.ConfigurableLogger(); const saveSession = new import_core20.SaveSession(); const gitService = new import_core20.GitService(); try { const isRepoResult = await gitService.isGitRepository(); if (!isRepoResult.ok || !isRepoResult.value) { logger2.warn("Current directory is not a Git repository."); const { continueWithoutGit } = await inquirer_default.customPrompt([ { type: "confirm", name: "continueWithoutGit", message: "Do you want to continue without Git integration?", default: false } ]); if (!continueWithoutGit) { logger2.warn("Session save cancelled."); return; } const sessionDetails2 = await promptSessionDetails(); const projectRoot2 = process.cwd(); await handleSessionSave({ sessionDetails: sessionDetails2, projectRoot: projectRoot2, git: { branch: "no-git", commit: "no-git", isDirty: false, stashId: null }, saveSession, logger: logger2 }); return; } const gitStatusResult = await gitService.getStatus(); if (!gitStatusResult.ok) { logger2.error("Failed to get Git status", { error: gitStatusResult.error }); return; } const gitStatus = gitStatusResult.value; if (gitStatus.isDirty) { logger2.warn("\u26A0\uFE0F Repository has uncommitted changes:"); gitStatus.dirtyFiles.forEach((file) => { logger2.plainLog(` ${file.status}: ${file.path}`); }); const hasNewFiles = gitStatus.newFiles.length > 0; const hasDeletedFiles = gitStatus.deletedFiles.length > 0; const hasUntrackedFiles = gitStatus.untrackedFiles.length > 0; const canStash = !hasNewFiles && !hasDeletedFiles && !hasUntrackedFiles; const { dirtyAction } = await promptDirtyState(gitStatus, canStash); if (dirtyAction === "cancel") { logger2.warn("Session save cancelled."); return; } if (dirtyAction === "commit") { const configResult = await gitService.isGitConfigured(); if (!configResult.ok) { logger2.error("Failed to check Git configuration", { error: configResult.error }); logger2.warn("Session save cancelled."); return; } if (!configResult.value) { logger2.error("Git is not properly configured for commits."); logger2.warn("Please configure Git with your name and email:"); logger2.warn(' git config --global user.name "Your Name"'); logger2.warn( ' git config --global user.email "your.email@example.com"' ); const { configureGit } = await inquirer_default.customPrompt([ { type: "confirm", name: "configureGit", message: "Would you like to configure Git now?", default: false } ]); if (configureGit) { const { userName, userEmail } = await inquirer_default.customPrompt([ { type: "input", name: "userName", message: "Enter your name for Git:", validate: (input) => { if (!input.trim()) { return "Name is required"; } return true; } }, { type: "input", name: "userEmail", message: "Enter your email for Git:", validate: (input) => { if (!input.trim()) { return "Email is required"; } return true; } } ]); const terminal = new import_core20.Terminal(); await terminal.execute(`git config user.name "${userName}"`); await terminal.execute(`git config user.email "${userEmail}"`); logger2.log("Git configured successfully."); } else { logger2.warn("Session save cancelled."); return; } } const { commitMessage } = await inquirer_default.customPrompt([ { type: "input", name: "commitMessage", message: "Enter commit message:", validate: (input) => { if (!input.trim()) { return "Commit message is required"; } return true; } } ]); logger2.log(" Committing changes..."); const commitResult = await gitService.commitChanges(commitMessage); if (!commitResult.ok) { logger2.error("Failed to commit changes", { error: commitResult.error, message: commitResult.error.message }); logger2.warn("Git commit failed. This might be due to:"); logger2.warn(" - No changes to commit"); logger2.warn(" - Git configuration issues"); logger2.warn(" - Repository permissions"); logger2.warn( 'Consider using "stash" instead or check your git status.' ); const { retryAction } = await inquirer_default.customPrompt([ { type: "list", name: "retryAction", message: "What would you like to do?", choices: [ { name: "Try stashing instead", value: "stash" }, { name: "Cancel session save", value: "cancel" } ] } ]); if (retryAction === "stash") { logger2.log("Attempting to stash changes..."); const stashResult = await gitService.createStash( "Session save stash" ); if (!stashResult.ok) { logger2.error("Failed to stash changes", { error: stashResult.error }); logger2.warn("Session save cancelled."); return; } logger2.log("Changes stashed successfully."); } else { logger2.warn("Session save cancelled."); return; } } else { logger2.log(" Changes committed successfully."); } } else if (dirtyAction === "stash") { const stashResult = await gitService.createStash("Session save stash"); if (!stashResult.ok) { logger2.error("Failed to stash changes", { error: stashResult.error }); return; } } } const gitState = await getCurrentGitState(gitService, logger2); if (!gitState) return; const sessionDetails = await promptSessionDetails(); const projectRoot = process.cwd(); await handleSessionSave({ sessionDetails, projectRoot, git: { ...gitState, isDirty: false, stashId: null }, saveSession, logger: logger2 }); } catch (error) { logger2.error("Unexpected error during session save", { error }); } } // packages/cli-interface/commands/session/resumeSession.ts var import_core21 = require("codestate-core"); async function resumeSessionCommand(sessionIdOrName) { const logger2 = new import_core21.ConfigurableLogger(); const resumeSession = new import_core21.ResumeSession(); const gitService = new import_core21.GitService(); const listSessions = new import_core21.ListSessions(); const saveSession = new import_core21.SaveSession(); const updateSession = new import_core21.UpdateSession(); const terminal = new import_core21.Terminal(); try { let targetSession = sessionIdOrName; if (!targetSession) { const sessionsResult = await listSessions.execute(); if (!sessionsResult.ok || sessionsResult.value.length === 0) { logger2.warn("No saved sessions found."); return; } const sessions = sessionsResult.value; const { selectedSession } = await inquirer_default.customPrompt([ { type: "list", name: "selectedSession", message: "Select a session to resume:", choices: sessions.map((s) => ({ name: `${s.name} (${s.projectRoot})`, value: s.id })) } ]); targetSession = selectedSession || ""; } if (!targetSession || !targetSession.trim()) { logger2.log("No session specified. Resume cancelled."); return; } const sessionResult = await resumeSession.execute(targetSession); if (!sessionResult.ok) { logger2.error("Failed to load session", { error: sessionResult.error }); return; } const session = sessionResult.value; logger2.plainLog(` \u{1F4CB} Resuming session: "${session.name}"`); const currentDir = process.cwd(); if (currentDir !== session.projectRoot) { logger2.warn(`You are in ${currentDir}`); logger2.log(`Session was saved from ${session.projectRoot}`); const { changeDirectory } = await inquirer_default.customPrompt([ { type: "confirm", name: "changeDirectory", message: "Do you want to change to the session directory?", default: true } ]); if (changeDirectory) { logger2.log(`Changing to ${session.projectRoot}...`); process.chdir(session.projectRoot); } else { logger2.log("Continuing in current directory..."); } } const isRepoResult = await gitService.isGitRepository(); if (!isRepoResult.ok || !isRepoResult.value) { logger2.warn("Current directory is not a Git repository."); logger2.plainLog( "Cannot restore Git state. Session resumed without Git integration." ); return; } const gitStatusResult = await gitService.getStatus(); if (!gitStatusResult.ok) { logger2.error("Failed to get Git status", { error: gitStatusResult.error }); return; } const gitStatus = gitStatusResult.value; if (gitStatus.isDirty) { logger2.warn("Current repository has uncommitted changes:"); gitStatus.dirtyFiles.forEach((file) => { logger2.plainLog(` ${file.status}: ${file.path}`); }); const hasNewFiles = gitStatus.newFiles.length > 0; const hasDeletedFiles = gitStatus.deletedFiles.length > 0; const hasUntrackedFiles = gitStatus.untrackedFiles.length > 0; const canStash = !hasNewFiles && !hasDeletedFiles && !hasUntrackedFiles; const { dirtyAction } = await promptDirtyState(gitStatus, canStash); if (dirtyAction === "cancel") { logger2.warn("Session resume cancelled."); return; } if (dirtyAction === "save") { logger2.log("Saving current work as new session..."); const sessionDetails = await promptSessionDetails(); const gitState = await getCurrentGitState(gitService, logger2); if (!gitState) return; await handleSessionSave({ sessionDetails, projectRoot: process.cwd(), git: { ...gitState, isDirty: false, stashId: null }, saveSession, logger: logger2 }); logger2.log("Current work saved. Proceeding with resume..."); } else if (dirtyAction === "discard") { await terminal.execute("git reset --hard"); await terminal.execute("git clean -fd"); logger2.log("Changes discarded. Proceeding with resume..."); } } const currentBranchResult = await gitService.getCurrentBranch(); if (currentBranchResult.ok && currentBranchResult.value !== session.git.branch) { await terminal.execute(`git checkout ${session.git.branch}`); } if (session.git.stashId) { logger2.log(`Applying stash ${session.git.stashId}...`); const applyStash = new import_core21.ApplyStash(); const stashResult = await applyStash.execute(session.git.stashId); if (stashResult.ok && stashResult.value.success) { } else { logger2.error("Failed to apply stash", { error: stashResult.ok ? stashResult.value.error : stashResult.error }); } } const getScriptsByRootPath = new import_core21.GetScriptsByRootPath(); const scriptsResult = await getScriptsByRootPath.execute( session.projectRoot ); if (scriptsResult.ok && scriptsResult.value.length > 0) { for (const script of scriptsResult.value) { const spawnResult = await terminal.spawnTerminal(script.script, { cwd: session.projectRoot, timeout: 5e3 // Short timeout for spawning }); if (!spawnResult.ok) { logger2.error( `Failed to spawn terminal for script: ${script.name || script.script}`, { error: spawnResult.error } ); } else { } await new Promise((resolve) => setTimeout(resolve, 500)); } } else { logger2.log("No scripts to execute."); } const getConfig = new import_core21.GetConfig(); const configResult = await getConfig.execute(); if (configResult.ok && configResult.value.ide) { const configuredIDE = configResult.value.ide; const openIDE = new import_core21.OpenIDE(); const ideResult = await openIDE.execute( configuredIDE, session.projectRoot ); if (ideResult.ok) { logger2.log(`IDE '${configuredIDE}' opened successfully`); if (session.files && session.files.length > 0) { const openFiles = new import_core21.OpenFiles(); const filesResult = await openFiles.execute({ ide: configuredIDE, projectRoot: session.projectRoot, files: session.files.map((file) => ({ path: file.path, line: file.cursor?.line, column: file.cursor?.column, isActive: file.isActive })) }); if (filesResult.ok) { } else { logger2.error("Failed to open files in IDE", { error: filesResult.error }); } } else { logger2.log("No files to open from session"); } } else { logger2.error(`Failed to open IDE '${configuredIDE}'`, { error: ideResult.error }); logger2.warn("Continuing without IDE..."); } } else { } logger2.log(` \u2705 Session "${session.name}" resumed successfully!`); if (session.notes) { logger2.plainLog(` \u{1F4DD} Notes: ${session.notes}`); } if (session.tags.length > 0) { logger2.log(`\u{1F3F7}\uFE0F Tags: ${session.tags.join(", ")}`); } } catch (error) { logger2.error("Unexpected error during session resume", { error }); } } // packages/cli-interface/commands/session/updateSession.ts var import_core22 = require("codestate-core"); async function updateSessionCommand(sessionIdOrName) { const logger2 = new import_core22.ConfigurableLogger(); const updateSession = new import_core22.UpdateSession(); const gitService = new import_core22.GitService(); const terminal = new import_core22.Terminal(); try { let targetSession = sessionIdOrName; if (!targetSession) { const listSessions = new import_core22.ListSessions(); const sessionsResult = await listSessions.execute(); if (!sessionsResult.ok || sessionsResult.value.length === 0) { logger2.warn("No saved sessions found."); return; } const sessions = sessionsResult.value; const { selectedSession } = await inquirer_default.customPrompt([ { type: "list", name: "selectedSession", message: "Select a session to update:", choices: sessions.map((s) => ({ name: `${s.name} (${s.projectRoot})`, value: s.id })) } ]); targetSession = selectedSession || ""; } if (!targetSession || !targetSession.trim()) { logger2.log("No session specified. Update cancelled."); return; } const sessionResult = await updateSession.execute(targetSession, {}); if (!sessionResult.ok) { logger2.error("Failed to load session", { error: sessionResult.error }); return; } const session = sessionResult.value; logger2.plainLog(` \u{1F4CB} Updating session: "${session.name}"`); logger2.log(`\u2705 Project: ${session.projectRoot}`); logger2.log(`\u2705 Branch: ${session.git.branch}`); logger2.log(`\u2705 Commit: ${session.git.commit}`); const currentDir = process.cwd(); if (currentDir !== session.projectRoot) { logger2.warn(`You are in ${currentDir}`); logger2.log(`Session was saved from ${session.projectRoot}`); const { changeDirectory } = await inquirer_default.customPrompt([ { type: "confirm", name: "changeDirectory", message: "Do you want to change to the session directory?", default: true } ]); if (changeDirectory) { logger2.log(`Changing to ${session.projectRoot}...`); process.chdir(session.projectRoot); } else { logger2.log("Continuing in current directory..."); } } const isRepoResult = await gitService.isGitRepository(); if (!isRepoResult.ok || !isRepoResult.value) { logger2.warn("Current directory is not a Git repository."); logger2.plainLog("Cannot update Git state. Session update cancelled."); return; } const gitStatusResult = await gitService.getStatus(); if (!gitStatusResult.ok) { logger2.error("Failed to get Git status", { error: gitStatusResult.error }); return; } const gitStatus = gitStatusResult.value; if (gitStatus.isDirty) { logger2.warn("\u26A0\uFE0F Current repository has uncommitted changes:"); gitStatus.dirtyFiles.forEach((file) => { logger2.plainLog(` ${file.status}: ${file.path}`); }); const hasNewFiles = gitStatus.newFiles.length > 0; const hasDeletedFiles = gitStatus.deletedFiles.length > 0; const hasUntrackedFiles = gitStatus.untrackedFiles.length > 0; const canStash = !hasNewFiles && !hasDeletedFiles && !hasUntrackedFiles; const { dirtyAction } = await promptDirtyState(gitStatus, canStash); if (dirtyAction === "cancel") { logger2.warn("Session update cancelled."); return; } if (dirtyAction === "commit") { const { commitMessage } = await inquirer_default.customPrompt([ { type: "input", name: "commitMessage", message: "Enter commit message:", validate: (input) => { if (!input.trim()) { return "Commit message is required"; } return true; } } ]); logger2.log(" Committing changes..."); const commitResult = await gitService.commitChanges(commitMessage); if (!commitResult.ok) { logger2.error("Failed to commit changes", { error: commitResult.error }); logger2.warn("Session update cancelled."); return; } logger2.log(" Changes committed successfully"); } else if (dirtyAction === "stash") { logger2.log("Stashing changes..."); const stashResult = await gitService.createStash( "Session update stash" ); if (!stashResult.ok || !stashResult.value.success) { logger2.error("Failed to stash changes", { error: stashResult.error }); logger2.warn("Session update cancelled."); return; } logger2.log("Changes stashed successfully"); } } const gitState = await getCurrentGitState(gitService, logger2); if (!gitState) { logger2.error("Failed to capture Git state"); return; } const sessionDetails = await promptSessionDetails({ name: session.name, // Session name is immutable notes: session.notes || "", tags: session.tags.join(", ") // Convert array to string for prompt }); const updateResult = await updateSession.execute(targetSession, { notes: sessionDetails.sessionNotes, tags: sessionDetails.sessionTags.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0), git: gitState, files: [], // Empty array in CLI mode extensions: {} }); if (!updateResult.ok) { logger2.error("Failed to update session", { error: updateResult.error }); return; } const updatedSession = updateResult.value; logger2.log(` \u2705 Session "${updatedSession.name}" updated successfully!`); if (updatedSession.notes) { logger2.plainLog(` \u{1F4DD} Notes: ${updatedSession.notes}`); } if (updatedSession.tags.length > 0) { logger2.log(`\u{1F3F7}\uFE0F Tags: ${updatedSession.tags.join(", ")}`); } } catch (error) { logger2.error("Unexpected error during session update", { error }); } } // packages/cli-interface/commands/session/listSessions.ts var import_core23 = require("codestate-core"); async function listSessionsCommand() { const logger2 = new import_core23.ConfigurableLogger(); const listSessions = new import_core23.ListSessions(); try { logger2.log("\u{1F4CB} Available Sessions:"); logger2.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"); const result = await listSessions.execute(); if (!result.ok) { logger2.error("Failed to list sessions", { error: result.error }); return; } const sessions = result.value; if (sessions.length === 0) { logger2.log("No sessions found."); return; } const sessionsByProject = sessions.reduce((acc, session) => { const projectPath = session.projectRoot; if (!acc[projectPath]) { acc[projectPath] = []; } acc[projectPath].push(session); return acc; }, {}); Object.entries(sessionsByProject).forEach( ([projectPath, projectSessions]) => { logger2.log( ` \u{1F4C1} ${projectPath} (${projectSessions.length} session${projectSessions.length > 1 ? "s" : ""})` ); logger2.log("\u2500".repeat(projectPath.length + 10)); projectSessions.forEach((session) => { const tags = session.tags.length > 0 ? ` [${session.tags.join(", ")}]` : ""; const notes = session.notes ? ` - ${session.notes}` : ""; logger2.log(` \u2022 ${session.name}${tags}${notes}`); logger2.log( ` ID: ${session.id} | Created: ${new Date( session.createdAt ).toLocaleString()}` ); if (session.git) { logger2.log( ` Git: ${session.git.branch} (${session.git.commit.substring( 0, 8 )})` ); } }); } ); logger2.log( ` Total: ${sessions.length} session${sessions.length > 1 ? "s" : ""}` ); } catch (error) { logger2.error("Unexpected error while listing sessions", { error }); } } // packages/cli-interface/commands/session/deleteSession.ts var import_core24 = require("codestate-core"); async function deleteSessionCommand(sessionIdOrName) { const logger2 = new import_core24.ConfigurableLogger(); const deleteSession = new import_core24.DeleteSession(); const listSessions = new import_core24.Lis