/** * Fetch NanoClaw upstream changes without merging. * Safe to run on a cron — read-only git operation. * * Usage: * npx tsx scripts/fetch-upstream.ts * * Output: prints divergence summary (commits in nanoclaw/main not in HEAD) * * Typical cron (weekly, run as agent user): * 0 3 * * 1 cd /home//-ai && npx tsx scripts/fetch-upstream.ts >> logs/upstream.log 2>&1 */ import { formatDisplayDate } from '../src/display-date.js'; import { classificationHint } from '../src/upstream/classify.js'; import { fetchRemote, getUpstreamConfig, readEnvFlag, remoteExists, } from '../src/upstream/git.js'; import { getUpstreamStatus } from '../src/upstream/status.js'; function main(): void { const config = getUpstreamConfig({ repoDir: process.cwd() }); if (!readEnvFlag(config.repoDir, 'NANOCLAW_UPSTREAM_ENABLED')) { console.log('NANOCLAW_UPSTREAM_ENABLED=false — skipping fetch'); process.exit(0); } if (!remoteExists(config.repoDir, config.remoteName)) { console.error(`Remote '${config.remoteName}' not configured. Run: npx tsx setup/index.ts --step upstream --enable`); process.exit(1); } const timestamp = formatDisplayDate(new Date()); console.log(`[${timestamp}] Fetching ${config.remoteName}/${config.branch}...`); fetchRemote(config.repoDir, config.remoteName); const status = getUpstreamStatus(config); if (status.aheadCount === 0) { const behindText = status.behindCount > 0 ? ` (${status.behindCount} local commit(s) ahead of upstream)` : ''; console.log(`✓ Up to date with NanoClaw upstream${behindText}`); } else { console.log(`! ${status.aheadCount} upstream commit(s) available:`); for (const commit of status.commits) { console.log(`${commit.hash.slice(0, 7)} ${commit.subject}`); console.log(` ${commit.classification}: ${classificationHint(commit.classification)}`); } if (status.behindCount > 0) { console.log(` (${status.behindCount} local commit(s) ahead of upstream)`); } } } main();