codestate-cli
Version:
CodeState CLI - Configuration, Script, and Git Management
1,418 lines (1,376 loc) • 55.5 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 __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