#!/usr/bin/env node // weekly-kb-cron.mjs — Local cron wrapper for weekly KB maintenance. // Runs sitemap polling + change report. If critical/high findings exist, // spawns a local Claude Code session to update stale reference files. // // Crontab: 23 4 * * 3 node /path/to/weekly-kb-cron.mjs >> /tmp/kb-cron.log 2>&1 // // Zero npm dependencies. Uses only node builtins. import { execFileSync, execSync } from 'node:child_process'; import { readFileSync, existsSync, appendFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PLUGIN_ROOT = join(__dirname, '..', '..'); const DATA_DIR = join(__dirname, 'data'); const CLAUDE_BIN = '/Users/ktg/.local/bin/claude'; const LOG_FILE = '/tmp/kb-cron.log'; const MAX_UPDATE_FILES = Infinity; function log(msg) { const ts = new Date().toISOString(); const line = `[${ts}] ${msg}`; console.log(line); } function run(script, args = []) { const fullPath = join(__dirname, script); log(`Running ${script} ${args.join(' ')}`); try { execFileSync('node', [fullPath, ...args], { stdio: 'inherit', timeout: 10 * 60 * 1000, cwd: PLUGIN_ROOT, }); } catch (err) { log(`ERROR: ${script} failed: ${err.message}`); process.exit(1); } } // --- Step 1: Sitemap polling + discovery --- log('=== Weekly KB Cron Start ==='); run('run-weekly-update.mjs', ['--force', '--discover']); // --- Step 2: Read change report --- const reportPath = join(DATA_DIR, 'change-report.json'); if (!existsSync(reportPath)) { log('No change report found. Exiting.'); process.exit(0); } const report = JSON.parse(readFileSync(reportPath, 'utf8')); const { critical = 0, high = 0, medium = 0, low = 0 } = report.by_priority || {}; log(`Change report: ${critical} critical, ${high} high, ${medium} medium, ${low} low`); // --- Step 3: If critical/high exist, spawn Claude for updates --- if (critical + high === 0) { log('No critical/high findings. Committing data updates only.'); try { execSync('git add scripts/kb-update/data/', { cwd: PLUGIN_ROOT, stdio: 'pipe' }); const status = execSync('git status --porcelain scripts/kb-update/data/', { cwd: PLUGIN_ROOT, encoding: 'utf8' }); if (status.trim()) { execSync( 'git commit -m "docs(architect): weekly KB poll — no stale files"', { cwd: PLUGIN_ROOT, stdio: 'pipe' } ); execSync('git push origin main', { cwd: PLUGIN_ROOT, stdio: 'pipe' }); log('Data committed and pushed.'); } else { log('No data changes to commit.'); } } catch (err) { log(`Git error: ${err.message}`); } log('=== Weekly KB Cron Done ==='); process.exit(0); } // Build list of stale files (critical + high only, max MAX_UPDATE_FILES) const staleFiles = (report.files || []) .filter(f => f.priority === 'critical' || f.priority === 'high' || f.priority === 'medium') .slice(0, MAX_UPDATE_FILES); log(`Spawning Claude to update ${staleFiles.length} stale files...`); const fileList = staleFiles.map(f => { const urls = (f.changed_urls || []).slice(0, 5).join('\n '); return `- ${f.path} [${f.priority}]\n Changed URLs:\n ${urls}`; }).join('\n'); const prompt = `Du er Cosmo Skyberg. Oppdater ${staleFiles.length} stale kunnskapsreferanser i ms-ai-architect pluginen. Arbeidsmappe: ${PLUGIN_ROOT} ## Filer å oppdatere ${fileList} ## For HVER fil 1. Les filen med Read 2. Bruk microsoft_docs_fetch på de endrede kilde-URLene listet over 3. Bruk microsoft_docs_search for supplerende info 4. Oppdater filen med Edit: - Oppdater "Last updated" til ${new Date().toISOString().slice(0, 7)} - Oppdater utdaterte fakta, priser, datoer - Bevar eksisterende struktur og seksjoner - Marker oppdatert innhold med "Verified (MCP ${new Date().toISOString().slice(0, 7)})" ## Etter alle oppdateringer 1. Kjør: node scripts/kb-update/build-registry.mjs --merge 2. Kjør: node scripts/kb-update/report-changes.mjs 3. git add skills/ scripts/kb-update/data/ 4. git commit -m "docs(architect): weekly KB update — ${staleFiles.length} files refreshed Co-Authored-By: Claude Sonnet 4.6 " 5. git push origin main ## Regler - Aldri slett filer, kun oppdater - Bruk Edit, ikke Write - Bevar all eksisterende struktur`; try { execFileSync(CLAUDE_BIN, [ '-p', prompt, '--model', 'sonnet', '--permission-mode', 'acceptEdits', '--allowedTools', 'Read,Edit,Bash,Glob,Grep,mcp__microsoft-learn__microsoft_docs_search,mcp__microsoft-learn__microsoft_docs_fetch', '--max-turns', '200', ], { stdio: 'inherit', timeout: 60 * 60 * 1000, cwd: PLUGIN_ROOT, }); log('Claude session completed.'); } catch (err) { log(`Claude session error: ${err.message}`); process.exit(1); } log('=== Weekly KB Cron Done ===');