Build LinkedIn thought leadership with algorithmic understanding, strategic consistency, and AI-assisted content creation. Updated for the January 2026 360Brew algorithm change. 16 agents, 25 commands, 6 skills, 9 hooks, 24 reference docs. Personal data sanitized: voice samples generalized to template, high-engagement posts cleared, region-specific references replaced with placeholders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
3.8 KiB
JavaScript
125 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// Queue management library for linkedin-thought-leadership plugin
|
|
// Import: import { queueRead, queueToday, ... } from './queue-manager.mjs';
|
|
// Replaces python3 dependency with native Node.js JSON/Date operations
|
|
|
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const PLUGIN_ROOT = process.env.PLUGIN_ROOT || join(__dirname, '..', '..');
|
|
const QUEUE_FILE = join(PLUGIN_ROOT, 'assets', 'drafts', 'queue.json');
|
|
|
|
function ensureQueue() {
|
|
if (!existsSync(QUEUE_FILE)) {
|
|
mkdirSync(dirname(QUEUE_FILE), { recursive: true });
|
|
writeFileSync(QUEUE_FILE, JSON.stringify({ version: 1, queue: [] }, null, 2));
|
|
}
|
|
}
|
|
|
|
function readQueue() {
|
|
ensureQueue();
|
|
try {
|
|
const data = JSON.parse(readFileSync(QUEUE_FILE, 'utf-8'));
|
|
return data.queue || [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function writeQueue(queue) {
|
|
ensureQueue();
|
|
const data = JSON.parse(readFileSync(QUEUE_FILE, 'utf-8'));
|
|
data.queue = queue;
|
|
writeFileSync(QUEUE_FILE, JSON.stringify(data, null, 2));
|
|
}
|
|
|
|
function todayISO() {
|
|
return new Date().toISOString().slice(0, 10);
|
|
}
|
|
|
|
// Read all queue entries
|
|
export function queueRead() {
|
|
return readQueue();
|
|
}
|
|
|
|
// Get entries scheduled for today (status=scheduled only)
|
|
export function queueToday() {
|
|
const today = todayISO();
|
|
return readQueue().filter(e => e.scheduled_date === today && e.status === 'scheduled');
|
|
}
|
|
|
|
// Get entries for next N days (status=scheduled only)
|
|
export function queueUpcoming(days = 7) {
|
|
const today = todayISO();
|
|
const end = new Date();
|
|
end.setDate(end.getDate() + days);
|
|
const endStr = end.toISOString().slice(0, 10);
|
|
return readQueue()
|
|
.filter(e => e.status === 'scheduled' && e.scheduled_date >= today && e.scheduled_date <= endStr)
|
|
.sort((a, b) => (a.scheduled_date + (a.scheduled_time || '')).localeCompare(b.scheduled_date + (b.scheduled_time || '')));
|
|
}
|
|
|
|
// Add entry to queue
|
|
export function queueAdd(id, draftPath, schedDate, schedTime, pillar, format, hookPreview, charCount) {
|
|
const queue = readQueue().filter(e => e.id !== id);
|
|
queue.push({
|
|
id,
|
|
draft_path: draftPath,
|
|
scheduled_date: schedDate,
|
|
scheduled_time: schedTime,
|
|
pillar,
|
|
format,
|
|
hook_preview: hookPreview,
|
|
character_count: charCount,
|
|
status: 'scheduled',
|
|
created_at: todayISO()
|
|
});
|
|
writeQueue(queue);
|
|
return `Added: ${id}`;
|
|
}
|
|
|
|
// Update status of a queue entry
|
|
export function queueUpdateStatus(id, newStatus) {
|
|
const queue = readQueue();
|
|
const entry = queue.find(e => e.id === id);
|
|
if (entry) {
|
|
entry.status = newStatus;
|
|
writeQueue(queue);
|
|
return `Updated: ${id} -> ${newStatus}`;
|
|
}
|
|
return `Not found: ${id}`;
|
|
}
|
|
|
|
// Get overdue entries (past scheduled_date, still "scheduled")
|
|
export function queueOverdue() {
|
|
const today = todayISO();
|
|
return readQueue()
|
|
.filter(e => e.status === 'scheduled' && (e.scheduled_date || '9999') < today)
|
|
.sort((a, b) => (a.scheduled_date || '').localeCompare(b.scheduled_date || ''));
|
|
}
|
|
|
|
// Count entries by status
|
|
export function queueCount() {
|
|
const counts = {};
|
|
for (const e of readQueue()) {
|
|
const s = e.status || 'unknown';
|
|
counts[s] = (counts[s] || 0) + 1;
|
|
}
|
|
return counts;
|
|
}
|
|
|
|
// Format queue entries as readable summary
|
|
export function queueFormatSummary(entries) {
|
|
if (!entries || entries.length === 0) return '(none)';
|
|
return entries.map(e => {
|
|
const d = e.scheduled_date || '?';
|
|
const t = e.scheduled_time || '?';
|
|
const hook = (e.hook_preview || '').slice(0, 50);
|
|
const pillar = e.pillar || '?';
|
|
const fmt = e.format || '?';
|
|
const status = e.status || '?';
|
|
return ` ${d} ${t} | ${hook}... | ${pillar} (${fmt}) [${status}]`;
|
|
}).join('\n');
|
|
}
|