@@ -665,10 +665,10 @@ export async function fetchCalendarWithFallback(
665665 }
666666
667667 try {
668- console . log ( ` 🔄 [fetch-calendar] MCP primary attempt ${ attempt + 1 } /${ maxRetries + 1 } …` ) ;
668+ console . error ( ` 🔄 [fetch-calendar] MCP primary attempt ${ attempt + 1 } /${ maxRetries + 1 } …` ) ;
669669 const raw = await callMcpCalendarEvents ( from , to , resolved ) ;
670670 const events = raw . map ( normalizeMcpCalendarEvent ) ;
671- console . log ( ` ✅ [fetch-calendar] MCP primary succeeded — ${ events . length } events` ) ;
671+ console . error ( ` ✅ [fetch-calendar] MCP primary succeeded — ${ events . length } events` ) ;
672672
673673 return {
674674 events,
@@ -691,11 +691,11 @@ export async function fetchCalendarWithFallback(
691691 }
692692
693693 // ── Web fallback path ──────────────────────────────────────────────────
694- console . log ( ` 🔄 [fetch-calendar] Falling back to riksdagen.se/sv/kalendarium/…` ) ;
694+ console . error ( ` 🔄 [fetch-calendar] Falling back to riksdagen.se/sv/kalendarium/…` ) ;
695695 let fallbackError : string | undefined ;
696696 try {
697697 const events = await fetchWebCalendar ( from , to , resolved ) ;
698- console . log ( ` ✅ [fetch-calendar] Web fallback succeeded — ${ events . length } events` ) ;
698+ console . error ( ` ✅ [fetch-calendar] Web fallback succeeded — ${ events . length } events` ) ;
699699
700700 return {
701701 events,
@@ -737,25 +737,29 @@ const REPO_ROOT = path.resolve(__dirname, '..');
737737const CALENDAR_DIR = path . join ( REPO_ROOT , 'data' , 'calendar' ) ;
738738
739739/**
740- * Write a `CalendarFetchResult` to `data/calendar/{from}.json`.
740+ * Write a `CalendarFetchResult` to `data/calendar/{from}_{to} .json`.
741741 *
742742 * The file is an object with `{ manifest, events }` so that consumers can
743743 * load a single file and get both the data and the provenance record.
744+ * Including `to` in the filename prevents collisions when the same `from`
745+ * date is fetched with different ranges (e.g. week-ahead vs month-ahead).
744746 */
745747export function persistCalendarJson (
746748 from : string ,
747749 result : CalendarFetchResult ,
748750 outputDir : string = CALENDAR_DIR ,
749751) : string {
750752 fs . mkdirSync ( outputDir , { recursive : true } ) ;
751- const outputPath = path . join ( outputDir , `${ from } .json` ) ;
753+ const dateTo = result . manifest . dateTo ?? from ;
754+ const fileName = dateTo && dateTo !== from ? `${ from } _${ dateTo } .json` : `${ from } .json` ;
755+ const outputPath = path . join ( outputDir , fileName ) ;
752756 const payload = {
753757 schema : 'riksdagsmonitor-calendar/1.0' ,
754758 manifest : result . manifest ,
755759 events : result . events ,
756760 } ;
757761 fs . writeFileSync ( outputPath , JSON . stringify ( payload , null , 2 ) , 'utf8' ) ;
758- console . log ( ` 💾 [fetch-calendar] Persisted ${ result . events . length } events → ${ outputPath } ` ) ;
762+ console . error ( ` 💾 [fetch-calendar] Persisted ${ result . events . length } events → ${ outputPath } ` ) ;
759763 return outputPath ;
760764}
761765
@@ -791,7 +795,20 @@ export function formatManifestMarkdown(manifest: CalendarFetchManifest): string
791795 return lines . join ( '\n' ) ;
792796}
793797
794- /** Parse CLI argv into `{ from, to, persist }`. */
798+ /** Thrown by `parseCalendarArgs` for invalid CLI arguments (exit code 2). */
799+ export class CliArgsError extends Error {
800+ constructor ( message : string ) {
801+ super ( message ) ;
802+ this . name = 'CliArgsError' ;
803+ }
804+ }
805+
806+ /**
807+ * Parse CLI argv into `{ from, to, persist }`.
808+ *
809+ * Accepts `--to` (preferred) and `--tom` (Swedish alias, used in repo docs)
810+ * as the end-date flag. Throws `CliArgsError` for invalid arguments.
811+ */
795812export function parseCalendarArgs ( argv : readonly string [ ] ) : {
796813 from : string ;
797814 to : string ;
@@ -813,29 +830,31 @@ export function parseCalendarArgs(argv: readonly string[]): {
813830 }
814831 const ISO_DATE_RE = / ^ \d { 4 } - \d { 2 } - \d { 2 } $ / ;
815832 const from = flags . get ( 'from' ) ?? '' ;
816- const to = flags . get ( 'to' ) ?? '' ;
833+ // Accept both `--to` and `--tom` (Swedish alias used in repo docs).
834+ const to = flags . get ( 'to' ) ?? flags . get ( 'tom' ) ?? '' ;
817835 if ( ! ISO_DATE_RE . test ( from ) ) {
818- throw new Error ( `--from must be an ISO 8601 date (YYYY-MM-DD), got: "${ from } "` ) ;
836+ throw new CliArgsError ( `--from must be an ISO 8601 date (YYYY-MM-DD), got: "${ from } "` ) ;
819837 }
820838 if ( ! ISO_DATE_RE . test ( to ) ) {
821- throw new Error ( `--to must be an ISO 8601 date (YYYY-MM-DD), got: "${ to } "` ) ;
839+ throw new CliArgsError ( `--to must be an ISO 8601 date (YYYY-MM-DD), got: "${ to } "` ) ;
822840 }
823841 return { from, to, persist : booleans . has ( 'persist' ) } ;
824842}
825843
826844async function main ( ) : Promise < void > {
827845 const args = parseCalendarArgs ( process . argv . slice ( 2 ) ) ;
828- console . log ( `📅 [fetch-calendar] Fetching ${ args . from } → ${ args . to } ` ) ;
846+ console . error ( `📅 [fetch-calendar] Fetching ${ args . from } → ${ args . to } ` ) ;
829847
830848 const result = await fetchCalendarWithFallback ( args . from , args . to ) ;
831849
832- console . log ( formatManifestMarkdown ( result . manifest ) ) ;
850+ // Manifest is human-readable status info → stderr, not stdout.
851+ console . error ( formatManifestMarkdown ( result . manifest ) ) ;
833852
834853 if ( args . persist ) {
835854 persistCalendarJson ( args . from , result ) ;
836855 } else {
837856 // Print JSON to stdout for piping / agentic workflow consumption.
838- console . log ( JSON . stringify ( result , null , 2 ) ) ;
857+ process . stdout . write ( ` ${ JSON . stringify ( result , null , 2 ) } \n` ) ;
839858 }
840859
841860 if ( result . manifest . path === 'none' ) {
@@ -847,6 +866,7 @@ async function main(): Promise<void> {
847866if ( path . resolve ( fileURLToPath ( import . meta. url ) ) === path . resolve ( process . argv [ 1 ] ?? '' ) ) {
848867 main ( ) . catch ( ( err : unknown ) => {
849868 console . error ( '❌ [fetch-calendar] Fatal error:' , err instanceof Error ? err . message : err ) ;
850- process . exit ( 1 ) ;
869+ // Bad CLI arguments → exit code 2 (per module header & repo convention).
870+ process . exit ( err instanceof CliArgsError ? 2 : 1 ) ;
851871 } ) ;
852872}
0 commit comments