feat(linkedin-thought-leadership): v1.1.0 — Q2 2026 feature release
9 improvements across 3 tracks: Onboarding: /linkedin:onboarding wizard, README Quick Start rewrite Content Quality: voice drift scoring, industry angle variants, /linkedin:carousel, /linkedin:react multi-URL comparison Analytics: automated week-rollover, day-of-week heatmap, month-over-month reports 25→27 commands. All Q2 ROADMAP items completed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
abf7322200
commit
1a8cc1942c
33 changed files with 1726 additions and 236 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { join, resolve, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { AnalyticsBatch, WeeklyReport, PostAnalytics } from "../models/types.js";
|
||||
import type { AnalyticsBatch, WeeklyReport, MonthlyReport, PostAnalytics } from "../models/types.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ export function getAnalyticsRoot(): string {
|
|||
* Ensure required subdirectories exist under analytics root
|
||||
*/
|
||||
export function ensureDirectories(root: string): void {
|
||||
const directories = ["exports", "posts", "weekly-reports"];
|
||||
const directories = ["exports", "posts", "weekly-reports", "monthly-reports"];
|
||||
|
||||
if (!existsSync(root)) {
|
||||
mkdirSync(root, { recursive: true });
|
||||
|
|
@ -252,3 +252,39 @@ export function loadAllWeeklyReports(root: string): WeeklyReport[] {
|
|||
b.week.localeCompare(a.week)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize month string to only allow YYYY-MM format
|
||||
*/
|
||||
function sanitizeMonth(month: string): string {
|
||||
if (!/^\d{4}-\d{2}$/.test(month)) {
|
||||
throw new Error(`Invalid month format: ${month}. Expected YYYY-MM`);
|
||||
}
|
||||
return month;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a monthly report to disk
|
||||
*/
|
||||
export function saveMonthlyReport(root: string, report: MonthlyReport): string {
|
||||
ensureDirectories(root);
|
||||
const reportsDir = join(root, "monthly-reports");
|
||||
const month = sanitizeMonth(report.month);
|
||||
const filename = `${month}.json`;
|
||||
const filepath = join(reportsDir, filename);
|
||||
verifyPathWithinDirectory(filepath, reportsDir);
|
||||
writeFileSync(filepath, JSON.stringify(report, null, 2), "utf-8");
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a specific monthly report by month identifier
|
||||
*/
|
||||
export function loadMonthlyReport(root: string, month: string): MonthlyReport | null {
|
||||
month = sanitizeMonth(month);
|
||||
const reportsDir = join(root, "monthly-reports");
|
||||
const filepath = join(reportsDir, `${month}.json`);
|
||||
if (!existsSync(filepath)) return null;
|
||||
const content = readFileSync(filepath, "utf-8");
|
||||
return JSON.parse(content) as MonthlyReport;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue