diff --git a/src/hooks/bundled/workspace-sync/handler.test.ts b/src/hooks/bundled/workspace-sync/handler.test.ts index be5fcc30b..dd81ff59a 100644 --- a/src/hooks/bundled/workspace-sync/handler.test.ts +++ b/src/hooks/bundled/workspace-sync/handler.test.ts @@ -1,8 +1,23 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import workspaceSyncHandler from "./handler.js"; import * as rclone from "../../../infra/rclone.js"; +const mockLog = vi.hoisted(() => ({ + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + trace: vi.fn(), + fatal: vi.fn(), + raw: vi.fn(), + child: vi.fn(), + subsystem: "workspace-sync", +})); + +vi.mock("../../../logging/subsystem.js", () => ({ + createSubsystemLogger: vi.fn(() => mockLog), +})); + vi.mock("../../../infra/rclone.js", () => ({ isRcloneInstalled: vi.fn(), isRcloneConfigured: vi.fn(), @@ -18,6 +33,9 @@ vi.mock("../../../routing/session-key.js", () => ({ resolveAgentIdFromSessionKey: vi.fn(() => "default"), })); +// Import after mocks are set up +const { default: workspaceSyncHandler } = await import("./handler.js"); + describe("workspace-sync hook handler", () => { beforeEach(() => { vi.clearAllMocks(); @@ -126,7 +144,6 @@ describe("workspace-sync hook handler", () => { it("warns when rclone is not installed", async () => { vi.mocked(rclone.isRcloneInstalled).mockResolvedValue(false); - const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); const event = { type: "session", @@ -148,7 +165,7 @@ describe("workspace-sync hook handler", () => { await workspaceSyncHandler(event as never); expect(rclone.isRcloneInstalled).toHaveBeenCalled(); - expect(warnSpy).toHaveBeenCalledWith("[workspace-sync] rclone not installed, skipping sync"); + expect(mockLog.warn).toHaveBeenCalledWith("rclone not installed, skipping sync"); }); it("warns when rclone is not configured", async () => { @@ -166,7 +183,6 @@ describe("workspace-sync hook handler", () => { onSessionStart: true, onSessionEnd: false, }); - const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); const event = { type: "session", @@ -187,7 +203,7 @@ describe("workspace-sync hook handler", () => { await workspaceSyncHandler(event as never); - expect(warnSpy).toHaveBeenCalledWith( + expect(mockLog.warn).toHaveBeenCalledWith( expect.stringContaining('rclone not configured for remote "cloud"'), ); }); @@ -208,7 +224,6 @@ describe("workspace-sync hook handler", () => { onSessionEnd: false, }); vi.mocked(rclone.runBisync).mockResolvedValue({ ok: true }); - vi.spyOn(console, "log").mockImplementation(() => {}); const event = { type: "session", @@ -255,7 +270,6 @@ describe("workspace-sync hook handler", () => { onSessionEnd: true, }); vi.mocked(rclone.runBisync).mockResolvedValue({ ok: true }); - vi.spyOn(console, "log").mockImplementation(() => {}); const event = { type: "session", @@ -298,8 +312,6 @@ describe("workspace-sync hook handler", () => { ok: false, error: "bisync requires --resync on first run", }); - vi.spyOn(console, "log").mockImplementation(() => {}); - const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); const event = { type: "session", @@ -320,8 +332,8 @@ describe("workspace-sync hook handler", () => { await workspaceSyncHandler(event as never); - expect(warnSpy).toHaveBeenCalledWith( - expect.stringContaining("First sync requires manual --resync"), + expect(mockLog.warn).toHaveBeenCalledWith( + expect.stringContaining("first sync requires manual --resync"), ); }); }); diff --git a/src/hooks/bundled/workspace-sync/handler.ts b/src/hooks/bundled/workspace-sync/handler.ts index 9edff53cb..fc492ed0d 100644 --- a/src/hooks/bundled/workspace-sync/handler.ts +++ b/src/hooks/bundled/workspace-sync/handler.ts @@ -8,6 +8,7 @@ import type { MoltbotConfig } from "../../../config/config.js"; import { resolveAgentWorkspaceDir } from "../../../agents/agent-scope.js"; import { resolveAgentIdFromSessionKey } from "../../../routing/session-key.js"; +import { createSubsystemLogger } from "../../../logging/subsystem.js"; import type { HookHandler } from "../../hooks.js"; import { isRcloneInstalled, @@ -16,6 +17,8 @@ import { runBisync, } from "../../../infra/rclone.js"; +const log = createSubsystemLogger("workspace-sync"); + /** * Sync workspace on session start or end */ @@ -55,13 +58,13 @@ const workspaceSyncHandler: HookHandler = async (event) => { return; } - console.log(`[workspace-sync] Triggered on session ${event.action}`); + log.info(`triggered on session ${event.action}`); try { // Check if rclone is installed const installed = await isRcloneInstalled(); if (!installed) { - console.warn("[workspace-sync] rclone not installed, skipping sync"); + log.warn("rclone not installed, skipping sync"); return; } @@ -74,15 +77,11 @@ const workspaceSyncHandler: HookHandler = async (event) => { // Check if rclone is configured if (!isRcloneConfigured(resolved.configPath, resolved.remoteName)) { - console.warn( - `[workspace-sync] rclone not configured for remote "${resolved.remoteName}", skipping sync`, - ); + log.warn(`rclone not configured for remote "${resolved.remoteName}", skipping sync`); return; } - console.log( - `[workspace-sync] Syncing ${resolved.remoteName}:${resolved.remotePath} <-> ${resolved.localPath}`, - ); + log.info(`syncing ${resolved.remoteName}:${resolved.remotePath} <-> ${resolved.localPath}`); // Run sync const result = await runBisync({ @@ -96,22 +95,20 @@ const workspaceSyncHandler: HookHandler = async (event) => { }); if (result.ok) { - console.log("[workspace-sync] Sync completed successfully"); + log.info("sync completed successfully"); if (result.filesTransferred) { - console.log(`[workspace-sync] Files transferred: ${result.filesTransferred}`); + log.info(`files transferred: ${result.filesTransferred}`); } } else { // Check if this is a first-run issue if (result.error?.includes("--resync")) { - console.warn( - "[workspace-sync] First sync requires manual --resync. Run: moltbot workspace sync --resync", - ); + log.warn("first sync requires manual --resync. Run: moltbot workspace sync --resync"); } else { - console.error(`[workspace-sync] Sync failed: ${result.error}`); + log.error(`sync failed: ${result.error}`); } } } catch (err) { - console.error("[workspace-sync] Error:", err instanceof Error ? err.message : String(err)); + log.error(`error: ${err instanceof Error ? err.message : String(err)}`); } };