@stackmemoryai/stackmemory
Version:
Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a
132 lines (131 loc) • 4.02 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { trace } from "./debug-trace.js";
import { logger } from "../monitoring/logger.js";
function _getEnv(key, defaultValue) {
const value = process.env[key];
if (value === void 0) {
if (defaultValue !== void 0) return defaultValue;
throw new Error(`Environment variable ${key} is required`);
}
return value;
}
function _getOptionalEnv(key) {
return process.env[key];
}
function wrapCommand(command) {
const originalAction = command.action.bind(command);
command.action(async function(...args) {
const commandPath = getCommandPath(command);
const options = args[args.length - 1];
const commandArgs = args.slice(0, -1);
const context = {
command: commandPath,
args: commandArgs,
options: typeof options === "object" ? options : {},
cwd: process.cwd(),
env: {
NODE_ENV: process.env["NODE_ENV"],
DEBUG_TRACE: process.env["DEBUG_TRACE"],
LINEAR_API_KEY: process.env["LINEAR_API_KEY"] ? "[SET]" : "[NOT SET]"
},
timestamp: (/* @__PURE__ */ new Date()).toISOString()
};
logger.info(`CLI Command: ${commandPath}`, context);
await trace.command(commandPath, context, async () => {
try {
const _result = await originalAction.apply(null, args);
logger.info(`CLI Command Completed: ${commandPath}`, {
duration: trace.exportTraces().find((t) => t.name === commandPath)?.duration
});
if (process.env["DEBUG_TRACE"] === "true") {
console.log(trace.getExecutionSummary());
}
} catch (error) {
logger.error(
`CLI Command Failed: ${commandPath}`,
error,
context
);
const lastError = trace.getLastError();
if (lastError) {
console.error("\n\u{1F4CD} Error occurred at:");
console.error(` ${lastError.name}`);
if (lastError.params) {
console.error(
" With params:",
JSON.stringify(lastError.params, null, 2)
);
}
console.error(" Error details:", lastError.error);
}
throw error;
}
});
});
command.commands.forEach((subcommand) => {
wrapCommand(subcommand);
});
return command;
}
function getCommandPath(command) {
const parts = [];
let current = command;
while (current) {
if (current.name()) {
parts.unshift(current.name());
}
current = current.parent;
}
return parts.join(" ");
}
function wrapProgram(program) {
program.exitOverride((err) => {
if (err.code === "commander.help" || err.code === "commander.version") {
process.exit(0);
}
logger.error("CLI Error", err, {
code: err.code,
exitCode: err.exitCode,
command: process.argv.slice(2).join(" ")
});
if (process.env["DEBUG_TRACE"] === "true") {
console.error("\n" + trace.getExecutionSummary());
}
process.exit(err.exitCode || 1);
});
program.hook("preAction", (thisCommand) => {
trace.reset();
const commandPath = getCommandPath(thisCommand);
logger.debug(`Preparing to execute: ${commandPath}`, {
args: thisCommand.args,
opts: thisCommand.opts()
});
});
program.hook("postAction", (thisCommand) => {
const commandPath = getCommandPath(thisCommand);
logger.debug(`Completed execution: ${commandPath}`);
});
program.commands.forEach((command) => {
wrapCommand(command);
});
return program;
}
function traceStep(name, fn) {
return trace.step(name, fn);
}
function traceQuery(sql, params, fn) {
return trace.traceSync("query", sql.substring(0, 100), params, fn);
}
function traceAPI(method, url, body, fn) {
return trace.api(method, url, body, fn);
}
export {
traceAPI,
traceQuery,
traceStep,
wrapCommand,
wrapProgram
};