From b8a248b0fb16ed4833d0a295124ae936c635cee8 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 9 Feb 2026 10:32:51 -0800 Subject: [PATCH] fix: CLI tools use Store class for v2 format compatibility (#235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit shared.ts was parsing lettabot-agent.json as v1 format directly, returning null for v2 stores. Now uses the Store class which handles v1/v2 transparently. Affects lettabot-message, lettabot-react, and lettabot-history. Written by Cameron ◯ Letta Code "Simplicity is the ultimate sophistication." -- Leonardo da Vinci --- src/cli/history-core.test.ts | 22 ++-------------------- src/cli/history.ts | 4 ++-- src/cli/message.ts | 2 +- src/cli/react.ts | 2 +- src/cli/shared.ts | 28 ++++++++-------------------- 5 files changed, 14 insertions(+), 44 deletions(-) diff --git a/src/cli/history-core.test.ts b/src/cli/history-core.test.ts index 8040cc8..7fb842d 100644 --- a/src/cli/history-core.test.ts +++ b/src/cli/history-core.test.ts @@ -1,9 +1,5 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { fetchDiscordHistory, fetchHistory, fetchSlackHistory, isValidLimit, parseFetchArgs } from './history-core.js'; -import { loadLastTarget } from './shared.js'; -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { tmpdir } from 'node:os'; const ORIGINAL_ENV = { ...process.env }; @@ -45,22 +41,8 @@ describe('isValidLimit', () => { }); }); -describe('loadLastTarget', () => { - it('loads the last message target from the store path', () => { - const dir = mkdtempSync(join(tmpdir(), 'lettabot-history-')); - const storePath = join(dir, 'lettabot-agent.json'); - writeFileSync( - storePath, - JSON.stringify({ lastMessageTarget: { channel: 'slack', chatId: 'C123' } }), - 'utf-8' - ); - - const target = loadLastTarget(storePath); - expect(target).toEqual({ channel: 'slack', chatId: 'C123' }); - - rmSync(dir, { recursive: true, force: true }); - }); -}); +// loadLastTarget is now backed by the Store class (handles v1/v2 transparently). +// Store-level tests in src/core/store.test.ts cover lastMessageTarget persistence. describe('fetchDiscordHistory', () => { it('formats Discord history responses', async () => { diff --git a/src/cli/history.ts b/src/cli/history.ts index 4d81ae8..98a1f57 100644 --- a/src/cli/history.ts +++ b/src/cli/history.ts @@ -11,7 +11,7 @@ import { loadConfig, applyConfigToEnv } from '../config/index.js'; const config = loadConfig(); applyConfigToEnv(config); import { fetchHistory, isValidLimit, parseFetchArgs } from './history-core.js'; -import { loadLastTarget, STORE_PATH } from './shared.js'; +import { loadLastTarget } from './shared.js'; async function fetchCommand(args: string[]): Promise { const parsed = parseFetchArgs(args); @@ -27,7 +27,7 @@ async function fetchCommand(args: string[]): Promise { } if (!channel || !chatId) { - const lastTarget = loadLastTarget(STORE_PATH); + const lastTarget = loadLastTarget(); if (lastTarget) { channel = channel || lastTarget.channel; chatId = chatId || lastTarget.chatId; diff --git a/src/cli/message.ts b/src/cli/message.ts index cf55ed4..2cf6faf 100644 --- a/src/cli/message.ts +++ b/src/cli/message.ts @@ -15,7 +15,7 @@ import { loadConfig, applyConfigToEnv } from '../config/index.js'; const config = loadConfig(); applyConfigToEnv(config); import { existsSync, readFileSync } from 'node:fs'; -import { loadLastTarget, STORE_PATH } from './shared.js'; +import { loadLastTarget } from './shared.js'; // Channel senders async function sendTelegram(chatId: string, text: string): Promise { diff --git a/src/cli/react.ts b/src/cli/react.ts index a5e94e9..b42882d 100644 --- a/src/cli/react.ts +++ b/src/cli/react.ts @@ -13,7 +13,7 @@ import { loadConfig, applyConfigToEnv } from '../config/index.js'; const config = loadConfig(); applyConfigToEnv(config); -import { loadLastTarget, STORE_PATH } from './shared.js'; +import { loadLastTarget } from './shared.js'; const EMOJI_ALIAS_TO_UNICODE: Record = { eyes: '👀', diff --git a/src/cli/shared.ts b/src/cli/shared.ts index 87cc8af..e3393b6 100644 --- a/src/cli/shared.ts +++ b/src/cli/shared.ts @@ -1,6 +1,4 @@ -import { existsSync, readFileSync } from 'node:fs'; -import { resolve } from 'node:path'; -import { getDataDir } from '../utils/paths.js'; +import { Store } from '../core/store.js'; export interface LastTarget { channel: string; @@ -8,21 +6,11 @@ export interface LastTarget { messageId?: string; } -interface AgentStore { - agentId?: string; - lastMessageTarget?: LastTarget; -} - -export const STORE_PATH = resolve(getDataDir(), 'lettabot-agent.json'); - -export function loadLastTarget(storePath: string = STORE_PATH): LastTarget | null { - try { - if (existsSync(storePath)) { - const store: AgentStore = JSON.parse(readFileSync(storePath, 'utf-8')); - return store.lastMessageTarget || null; - } - } catch { - // Ignore - } - return null; +/** + * Load the last message target from the agent store. + * Uses Store class which handles both v1 and v2 formats transparently. + */ +export function loadLastTarget(): LastTarget | null { + const store = new Store('lettabot-agent.json'); + return store.lastMessageTarget || null; }