From 5f4b61c7e4da126ae251cb1d8e58b1e63714d9b5 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Thu, 30 Jul 2026 20:03:50 -0400 Subject: [PATCH 1/3] fix: unify cron job commands and remove redundant path handling - Replace --chat with --message flag across all cron job definitions - Remove hardcoded 'run /reflection' in favor of --message - Remove unnecessary node path replacement (node is in PATH) - Remove log redirection (>>) from prepareCrontabCommand - Update persisted reflection-daily.json to match new command format - Update tests to reflect new prepareCrontabCommand behavior - Read schedulesDir from config instead of hardcoding in autoSchedule.js --- src/index.js | 4 ++-- src/scheduler/autoSchedule.js | 9 +++++---- src/scheduler/cron.js | 8 ++------ tests/unit/autoSchedule.test.js | 6 +++--- tests/unit/scheduler/cron.test.js | 25 +++++++------------------ 5 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/index.js b/src/index.js index 33ae5d50..a240ee04 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,7 @@ if (config.schedules.syncOnInit !== false) { const jobResult = Cron.add({ name: "reflection-daily", cron: "0 2 * * *", - command: `cd ${cwd} && node index.js --chat "/reflection"`, + command: `cd ${cwd} && node index.js --message run /reflection`, }); if (jobResult.added || !jobResult.error) { try { @@ -70,7 +70,7 @@ if (config.schedules.syncOnInit !== false) { const jobData = { name: "reflection-daily", cron: "0 2 * * *", - command: `cd ${cwd} && node index.js --chat "/reflection"`, + command: `cd ${cwd} && node index.js --message run /reflection`, enabled: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/src/scheduler/autoSchedule.js b/src/scheduler/autoSchedule.js index 93e2f042..73fdff30 100644 --- a/src/scheduler/autoSchedule.js +++ b/src/scheduler/autoSchedule.js @@ -4,11 +4,12 @@ import { Cron } from "./cron.js"; import { logger } from "../logger.js"; import { loadConfig } from "../config/loader.js"; -const cwd = loadConfig().cwd; +const config = loadConfig(); +const cwd = config.cwd; const JOB_CRON = "0 2 * * *"; -const SCHEDULES_DIR = "memory/schedules/"; +const SCHEDULES_DIR = config.memory?.schedulesDir || "memory/schedules/"; /** * Job definition shape for the daily reflection cron job. @@ -27,7 +28,7 @@ function createJobDefinition(cwd) { return { name: "reflection-daily", cron: JOB_CRON, - command: `cd ${cwd} && timeout 300 node index.js "run /reflection"`, + command: `cd ${cwd} && timeout 300 node index.js --message run /reflection`, }; } @@ -58,7 +59,7 @@ function persistJobFile(jobName, job, cwd) { const jobData = Object.freeze({ name: job.name, cron: job.cron, - command: `cd ${cwd} && timeout 300 node index.js "run /reflection"`, + command: `cd ${cwd} && timeout 300 node index.js --message run /reflection`, enabled: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/src/scheduler/cron.js b/src/scheduler/cron.js index 2ff44b4c..053a7608 100644 --- a/src/scheduler/cron.js +++ b/src/scheduler/cron.js @@ -1,7 +1,6 @@ import { execSync } from "node:child_process"; import { readdir, readFile } from "node:fs/promises"; import { join } from "node:path"; -import { getLogDirectory } from "../logger.js"; // Block delimiters for madz-managed crontab entries const BLOCK_START = "# --- BEGIN madz-schedules ---"; @@ -29,11 +28,8 @@ export function sanitizeCrontabCommand(command) { * @param {string} [logPath] - Optional log file path for output redirection * @returns {string} The prepared command */ -export function prepareCrontabCommand(command, logPath) { - const sanitized = sanitizeCrontabCommand(command); - const withAbsolutePath = sanitized.replace(/\bnode\b/g, "/usr/local/bin/node"); - const logFile = logPath || join(getLogDirectory(), "madz_cron.log"); - return `${withAbsolutePath} >> ${logFile} 2>&1`; +export function prepareCrontabCommand(command) { + return sanitizeCrontabCommand(command); } /** diff --git a/tests/unit/autoSchedule.test.js b/tests/unit/autoSchedule.test.js index 11177859..6ad2efb1 100644 --- a/tests/unit/autoSchedule.test.js +++ b/tests/unit/autoSchedule.test.js @@ -65,8 +65,8 @@ describe("setupAutoSchedule", () => { assert.strictEqual(capturedJob.cron, "0 2 * * *"); assert.ok(typeof capturedJob.command === "string"); assert.ok( - capturedJob.command.includes('node index.js "run /reflection"'), - "Command should include the run /reflection command", + capturedJob.command.includes("node index.js --message run /reflection"), + "Command should include the --message /reflection command", ); }); @@ -99,7 +99,7 @@ describe("setupAutoSchedule", () => { assert.strictEqual(content.cron, "0 2 * * *"); assert.ok( content.command.startsWith("cd ") && - content.command.includes('node index.js "run /reflection"'), + content.command.includes("node index.js --message run /reflection"), ); assert.strictEqual(content.enabled, true); assert.ok(content.createdAt); diff --git a/tests/unit/scheduler/cron.test.js b/tests/unit/scheduler/cron.test.js index d05eb9f3..00fd080c 100644 --- a/tests/unit/scheduler/cron.test.js +++ b/tests/unit/scheduler/cron.test.js @@ -1,12 +1,10 @@ import { describe, it } from "node:test"; import assert from "node:assert"; -import { join } from "node:path"; import { Cron, sanitizeCrontabCommand, prepareCrontabCommand, } from "../../../src/scheduler/cron.js"; -import { getLogDirectory } from "../../../src/logger.js"; describe("cron - sanitizeCrontabCommand", () => { it("strips carriage returns", () => { @@ -31,28 +29,19 @@ describe("cron - sanitizeCrontabCommand", () => { }); describe("cron - prepareCrontabCommand", () => { - it("replaces bare node with absolute path", () => { + it("returns the sanitized command as-is", () => { const result = prepareCrontabCommand("node index.js", "/tmp/test.log"); - assert.strictEqual(result, "/usr/local/bin/node index.js >> /tmp/test.log 2>&1"); + assert.strictEqual(result, "node index.js"); }); - it("uses provided log path", () => { + it("ignores logPath parameter", () => { const result = prepareCrontabCommand("node test.js", "/custom/path.log"); - assert.strictEqual(result, "/usr/local/bin/node test.js >> /custom/path.log 2>&1"); - }); - - it("defaults to logger directory when no log path provided", () => { - const logDir = getLogDirectory(); - const result = prepareCrontabCommand("node index.js"); - assert.strictEqual( - result, - `/usr/local/bin/node index.js >> ${join(logDir, "madz_cron.log")} 2>&1`, - ); + assert.strictEqual(result, "node test.js"); }); it("sanitizes newlines in command", () => { const result = prepareCrontabCommand("node index.js\r\n", "/tmp/test.log"); - assert.strictEqual(result, "/usr/local/bin/node index.js >> /tmp/test.log 2>&1"); + assert.strictEqual(result, "node index.js"); }); }); @@ -60,13 +49,13 @@ describe("cron - Cron.setLogPath", () => { it("sets the log path on the Cron instance", () => { Cron.setLogPath("/custom/log/path.log"); const result = prepareCrontabCommand("node test.js", "/custom/log/path.log"); - assert.strictEqual(result, "/usr/local/bin/node test.js >> /custom/log/path.log 2>&1"); + assert.strictEqual(result, "node test.js"); }); it("is idempotent — subsequent calls overwrite", () => { Cron.setLogPath("/first/path.log"); Cron.setLogPath("/second/path.log"); const result = prepareCrontabCommand("node test.js", "/second/path.log"); - assert.strictEqual(result, "/usr/local/bin/node test.js >> /second/path.log 2>&1"); + assert.strictEqual(result, "node test.js"); }); }); From 985d0ef8b189c696fe4b94adc1fcb8b3073c9de2 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Thu, 30 Jul 2026 20:49:46 -0400 Subject: [PATCH 2/3] fix: use src/index.js in cron commands (index.js doesn't exist at root) --- src/scheduler/autoSchedule.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scheduler/autoSchedule.js b/src/scheduler/autoSchedule.js index 73fdff30..5d1a321b 100644 --- a/src/scheduler/autoSchedule.js +++ b/src/scheduler/autoSchedule.js @@ -28,7 +28,7 @@ function createJobDefinition(cwd) { return { name: "reflection-daily", cron: JOB_CRON, - command: `cd ${cwd} && timeout 300 node index.js --message run /reflection`, + command: `cd ${cwd} && timeout 300 node src/index.js --message run /reflection`, }; } @@ -59,7 +59,7 @@ function persistJobFile(jobName, job, cwd) { const jobData = Object.freeze({ name: job.name, cron: job.cron, - command: `cd ${cwd} && timeout 300 node index.js --message run /reflection`, + command: `cd ${cwd} && timeout 300 node src/index.js --message run /reflection`, enabled: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), From 86509d8f73f8725767dccca5ea26ac58aaf2127d Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Fri, 31 Jul 2026 06:36:52 -0400 Subject: [PATCH 3/3] fix: correct test assertions for src/index.js path --- tests/unit/autoSchedule.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/autoSchedule.test.js b/tests/unit/autoSchedule.test.js index 6ad2efb1..e772438d 100644 --- a/tests/unit/autoSchedule.test.js +++ b/tests/unit/autoSchedule.test.js @@ -65,7 +65,7 @@ describe("setupAutoSchedule", () => { assert.strictEqual(capturedJob.cron, "0 2 * * *"); assert.ok(typeof capturedJob.command === "string"); assert.ok( - capturedJob.command.includes("node index.js --message run /reflection"), + capturedJob.command.includes("node src/index.js --message run /reflection"), "Command should include the --message /reflection command", ); }); @@ -99,7 +99,7 @@ describe("setupAutoSchedule", () => { assert.strictEqual(content.cron, "0 2 * * *"); assert.ok( content.command.startsWith("cd ") && - content.command.includes("node index.js --message run /reflection"), + content.command.includes("node src/index.js --message run /reflection"), ); assert.strictEqual(content.enabled, true); assert.ok(content.createdAt);