From 0855ff5f589da43e33c39aeb1c0024941ba5caa6 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Tue, 31 Mar 2026 08:39:03 +0530 Subject: [PATCH 1/5] fix: resolve chronicle.yaml only from cwd, not content dir Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/cli/utils/config.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/chronicle/src/cli/utils/config.ts b/packages/chronicle/src/cli/utils/config.ts index eba3ea1..0acdc38 100644 --- a/packages/chronicle/src/cli/utils/config.ts +++ b/packages/chronicle/src/cli/utils/config.ts @@ -15,21 +15,19 @@ export function resolveContentDir(contentFlag?: string): string { return path.resolve('content'); } -function resolveConfigPath(contentDir: string): string | null { +function resolveConfigPath(): string | null { const cwdPath = path.join(process.cwd(), 'chronicle.yaml'); if (fs.existsSync(cwdPath)) return cwdPath; - const contentPath = path.join(contentDir, 'chronicle.yaml'); - if (fs.existsSync(contentPath)) return contentPath; return null; } export function loadCLIConfig(contentDir: string): CLIConfig { - const configPath = resolveConfigPath(contentDir); + const configPath = resolveConfigPath(); if (!configPath) { console.log( chalk.red( - `Error: chronicle.yaml not found in '${process.cwd()}' or '${contentDir}'` + `Error: chronicle.yaml not found in '${process.cwd()}'` ) ); console.log(chalk.gray("Run 'chronicle init' to create one")); From f0d6038d836451b345921c411301f9f11117a4f0 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Tue, 31 Mar 2026 08:49:51 +0530 Subject: [PATCH 2/5] feat: add --config flag and remove -c short arg from CLI commands Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/cli/commands/build.ts | 7 +++++-- packages/chronicle/src/cli/commands/dev.ts | 8 +++++--- packages/chronicle/src/cli/commands/serve.ts | 7 +++++-- packages/chronicle/src/cli/commands/start.ts | 2 +- packages/chronicle/src/cli/utils/config.ts | 7 ++++--- packages/chronicle/src/server/vite-config.ts | 20 ++++++++++++-------- 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/packages/chronicle/src/cli/commands/build.ts b/packages/chronicle/src/cli/commands/build.ts index 185c2c0..13e5d67 100644 --- a/packages/chronicle/src/cli/commands/build.ts +++ b/packages/chronicle/src/cli/commands/build.ts @@ -1,18 +1,20 @@ import chalk from 'chalk'; import { Command } from 'commander'; -import { resolveContentDir } from '@/cli/utils/config'; +import { resolveConfigPath, resolveContentDir } from '@/cli/utils/config'; import { PACKAGE_ROOT } from '@/cli/utils/resolve'; import { linkContent } from '@/cli/utils/scaffold'; export const buildCommand = new Command('build') .description('Build for production') - .option('-c, --content ', 'Content directory') + .option('--content ', 'Content directory') + .option('--config ', 'Path to chronicle.yaml') .option( '--preset ', 'Deploy preset (vercel, cloudflare, node-server)' ) .action(async options => { const contentDir = resolveContentDir(options.content); + const configPath = resolveConfigPath(options.config); await linkContent(contentDir); console.log(chalk.cyan('Building for production...')); @@ -24,6 +26,7 @@ export const buildCommand = new Command('build') packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, + configPath: configPath ?? undefined, preset: options.preset }); diff --git a/packages/chronicle/src/cli/commands/dev.ts b/packages/chronicle/src/cli/commands/dev.ts index 82bbbe2..0ad70d6 100644 --- a/packages/chronicle/src/cli/commands/dev.ts +++ b/packages/chronicle/src/cli/commands/dev.ts @@ -2,16 +2,18 @@ import fs from 'node:fs'; import path from 'node:path'; import chalk from 'chalk'; import { Command } from 'commander'; -import { resolveContentDir } from '@/cli/utils/config'; +import { resolveConfigPath, resolveContentDir } from '@/cli/utils/config'; import { PACKAGE_ROOT } from '@/cli/utils/resolve'; import { linkContent } from '@/cli/utils/scaffold'; export const devCommand = new Command('dev') .description('Start development server') .option('-p, --port ', 'Port number', '3000') - .option('-c, --content ', 'Content directory') + .option('--content ', 'Content directory') + .option('--config ', 'Path to chronicle.yaml') .action(async options => { const contentDir = resolveContentDir(options.content); + const configPath = resolveConfigPath(options.config); const port = parseInt(options.port, 10); await linkContent(contentDir); @@ -21,7 +23,7 @@ export const devCommand = new Command('dev') const { createServer } = await import('vite'); const { createViteConfig } = await import('@/server/vite-config'); - const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir }); + const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, configPath: configPath ?? undefined }); const server = await createServer({ ...config, server: { ...config.server, port } diff --git a/packages/chronicle/src/cli/commands/serve.ts b/packages/chronicle/src/cli/commands/serve.ts index 65cfd33..3b63c06 100644 --- a/packages/chronicle/src/cli/commands/serve.ts +++ b/packages/chronicle/src/cli/commands/serve.ts @@ -1,19 +1,21 @@ import chalk from 'chalk'; import { Command } from 'commander'; -import { resolveContentDir } from '@/cli/utils/config'; +import { resolveConfigPath, resolveContentDir } from '@/cli/utils/config'; import { PACKAGE_ROOT } from '@/cli/utils/resolve'; import { linkContent } from '@/cli/utils/scaffold'; export const serveCommand = new Command('serve') .description('Build and start production server') .option('-p, --port ', 'Port number', '3000') - .option('-c, --content ', 'Content directory') + .option('--content ', 'Content directory') + .option('--config ', 'Path to chronicle.yaml') .option( '--preset ', 'Deploy preset (vercel, cloudflare, node-server)' ) .action(async options => { const contentDir = resolveContentDir(options.content); + const configPath = resolveConfigPath(options.config); const port = parseInt(options.port, 10); await linkContent(contentDir); @@ -24,6 +26,7 @@ export const serveCommand = new Command('serve') packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, + configPath: configPath ?? undefined, preset: options.preset }); diff --git a/packages/chronicle/src/cli/commands/start.ts b/packages/chronicle/src/cli/commands/start.ts index ed0ee85..9739ed1 100644 --- a/packages/chronicle/src/cli/commands/start.ts +++ b/packages/chronicle/src/cli/commands/start.ts @@ -7,7 +7,7 @@ import { linkContent } from '@/cli/utils/scaffold'; export const startCommand = new Command('start') .description('Start production server') .option('-p, --port ', 'Port number', '3000') - .option('-c, --content ', 'Content directory') + .option('--content ', 'Content directory') .action(async options => { const contentDir = resolveContentDir(options.content); const port = parseInt(options.port, 10); diff --git a/packages/chronicle/src/cli/utils/config.ts b/packages/chronicle/src/cli/utils/config.ts index 0acdc38..d04a768 100644 --- a/packages/chronicle/src/cli/utils/config.ts +++ b/packages/chronicle/src/cli/utils/config.ts @@ -15,14 +15,15 @@ export function resolveContentDir(contentFlag?: string): string { return path.resolve('content'); } -function resolveConfigPath(): string | null { +export function resolveConfigPath(configFlag?: string): string | null { + if (configFlag) return path.resolve(configFlag); const cwdPath = path.join(process.cwd(), 'chronicle.yaml'); if (fs.existsSync(cwdPath)) return cwdPath; return null; } -export function loadCLIConfig(contentDir: string): CLIConfig { - const configPath = resolveConfigPath(); +export function loadCLIConfig(contentDir: string, configFlag?: string): CLIConfig { + const configPath = resolveConfigPath(configFlag); if (!configPath) { console.log( diff --git a/packages/chronicle/src/server/vite-config.ts b/packages/chronicle/src/server/vite-config.ts index 636d3fa..a331b62 100644 --- a/packages/chronicle/src/server/vite-config.ts +++ b/packages/chronicle/src/server/vite-config.ts @@ -19,26 +19,30 @@ export interface ViteConfigOptions { packageRoot: string; projectRoot: string; contentDir: string; + configPath?: string; preset?: string; } -async function readChronicleConfig(projectRoot: string, contentDir: string): Promise { - for (const dir of [projectRoot, contentDir]) { - const filePath = path.join(dir, 'chronicle.yaml'); +async function readChronicleConfig(projectRoot: string, configPath?: string): Promise { + if (configPath) { try { - return await fs.readFile(filePath, 'utf-8'); + return await fs.readFile(configPath, 'utf-8'); } catch { - // not found, try next + return null; } } - return null; + try { + return await fs.readFile(path.join(projectRoot, 'chronicle.yaml'), 'utf-8'); + } catch { + return null; + } } export async function createViteConfig( options: ViteConfigOptions ): Promise { - const { packageRoot, projectRoot, contentDir, preset } = options; - const rawConfig = await readChronicleConfig(projectRoot, contentDir); + const { packageRoot, projectRoot, contentDir, configPath, preset } = options; + const rawConfig = await readChronicleConfig(projectRoot, configPath); return { root: packageRoot, From 3a9d126224cb9254bec8af57e93a9e3cb711ccba Mon Sep 17 00:00:00 2001 From: Rishabh Date: Tue, 31 Mar 2026 08:58:32 +0530 Subject: [PATCH 3/5] fix: throw error when explicit --config path is unreadable Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/server/vite-config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/chronicle/src/server/vite-config.ts b/packages/chronicle/src/server/vite-config.ts index a331b62..578e121 100644 --- a/packages/chronicle/src/server/vite-config.ts +++ b/packages/chronicle/src/server/vite-config.ts @@ -27,8 +27,8 @@ async function readChronicleConfig(projectRoot: string, configPath?: string): Pr if (configPath) { try { return await fs.readFile(configPath, 'utf-8'); - } catch { - return null; + } catch (error) { + throw new Error(`Failed to read config file '${configPath}': ${(error as Error).message}`); } } try { From 1e146a75c905dfbb6b1bbe0ffca20514f66cadef Mon Sep 17 00:00:00 2001 From: Rishabh Date: Wed, 1 Apr 2026 11:20:43 +0530 Subject: [PATCH 4/5] fix: rename configFlag to configPath and return undefined instead of null Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/cli/commands/build.ts | 2 +- packages/chronicle/src/cli/commands/dev.ts | 2 +- packages/chronicle/src/cli/commands/serve.ts | 2 +- packages/chronicle/src/cli/utils/config.ts | 16 ++++++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/chronicle/src/cli/commands/build.ts b/packages/chronicle/src/cli/commands/build.ts index 13e5d67..becb1a5 100644 --- a/packages/chronicle/src/cli/commands/build.ts +++ b/packages/chronicle/src/cli/commands/build.ts @@ -26,7 +26,7 @@ export const buildCommand = new Command('build') packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, - configPath: configPath ?? undefined, + configPath, preset: options.preset }); diff --git a/packages/chronicle/src/cli/commands/dev.ts b/packages/chronicle/src/cli/commands/dev.ts index 0ad70d6..35c6a1a 100644 --- a/packages/chronicle/src/cli/commands/dev.ts +++ b/packages/chronicle/src/cli/commands/dev.ts @@ -23,7 +23,7 @@ export const devCommand = new Command('dev') const { createServer } = await import('vite'); const { createViteConfig } = await import('@/server/vite-config'); - const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, configPath: configPath ?? undefined }); + const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, configPath }); const server = await createServer({ ...config, server: { ...config.server, port } diff --git a/packages/chronicle/src/cli/commands/serve.ts b/packages/chronicle/src/cli/commands/serve.ts index 3b63c06..2861678 100644 --- a/packages/chronicle/src/cli/commands/serve.ts +++ b/packages/chronicle/src/cli/commands/serve.ts @@ -26,7 +26,7 @@ export const serveCommand = new Command('serve') packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, - configPath: configPath ?? undefined, + configPath, preset: options.preset }); diff --git a/packages/chronicle/src/cli/utils/config.ts b/packages/chronicle/src/cli/utils/config.ts index d04a768..b8f73e5 100644 --- a/packages/chronicle/src/cli/utils/config.ts +++ b/packages/chronicle/src/cli/utils/config.ts @@ -15,17 +15,17 @@ export function resolveContentDir(contentFlag?: string): string { return path.resolve('content'); } -export function resolveConfigPath(configFlag?: string): string | null { - if (configFlag) return path.resolve(configFlag); +export function resolveConfigPath(configPath?: string): string | undefined { + if (configPath) return path.resolve(configPath); const cwdPath = path.join(process.cwd(), 'chronicle.yaml'); if (fs.existsSync(cwdPath)) return cwdPath; - return null; + return undefined; } -export function loadCLIConfig(contentDir: string, configFlag?: string): CLIConfig { - const configPath = resolveConfigPath(configFlag); +export function loadCLIConfig(contentDir: string, configPath?: string): CLIConfig { + const resolvedConfigPath = resolveConfigPath(configPath); - if (!configPath) { + if (!resolvedConfigPath) { console.log( chalk.red( `Error: chronicle.yaml not found in '${process.cwd()}'` @@ -35,7 +35,7 @@ export function loadCLIConfig(contentDir: string, configFlag?: string): CLIConfi process.exit(1); } - const config = parse(fs.readFileSync(configPath, 'utf-8')) as ChronicleConfig; + const config = parse(fs.readFileSync(resolvedConfigPath, 'utf-8')) as ChronicleConfig; - return { config, configPath, contentDir }; + return { config, configPath: resolvedConfigPath, contentDir }; } From 09d26284e5a4fdd8336c632e55700fed42d5d70b Mon Sep 17 00:00:00 2001 From: Rishabh Date: Wed, 1 Apr 2026 13:02:25 +0530 Subject: [PATCH 5/5] refactor: use fs promises and simplify resolveConfigPath Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/cli/utils/config.ts | 23 ++++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/chronicle/src/cli/utils/config.ts b/packages/chronicle/src/cli/utils/config.ts index b8f73e5..5e67272 100644 --- a/packages/chronicle/src/cli/utils/config.ts +++ b/packages/chronicle/src/cli/utils/config.ts @@ -1,4 +1,4 @@ -import fs from 'node:fs'; +import fs from 'node:fs/promises'; import path from 'node:path'; import chalk from 'chalk'; import { parse } from 'yaml'; @@ -17,25 +17,22 @@ export function resolveContentDir(contentFlag?: string): string { export function resolveConfigPath(configPath?: string): string | undefined { if (configPath) return path.resolve(configPath); - const cwdPath = path.join(process.cwd(), 'chronicle.yaml'); - if (fs.existsSync(cwdPath)) return cwdPath; return undefined; } -export function loadCLIConfig(contentDir: string, configPath?: string): CLIConfig { - const resolvedConfigPath = resolveConfigPath(configPath); +export async function loadCLIConfig(contentDir: string, configPath?: string): Promise { + const resolvedConfigPath = resolveConfigPath(configPath) + ?? path.join(process.cwd(), 'chronicle.yaml'); - if (!resolvedConfigPath) { + try { + const raw = await fs.readFile(resolvedConfigPath, 'utf-8'); + const config = parse(raw) as ChronicleConfig; + return { config, configPath: resolvedConfigPath, contentDir }; + } catch { console.log( - chalk.red( - `Error: chronicle.yaml not found in '${process.cwd()}'` - ) + chalk.red(`Error: chronicle.yaml not found at '${resolvedConfigPath}'`) ); console.log(chalk.gray("Run 'chronicle init' to create one")); process.exit(1); } - - const config = parse(fs.readFileSync(resolvedConfigPath, 'utf-8')) as ChronicleConfig; - - return { config, configPath: resolvedConfigPath, contentDir }; }