Files
evanpelle 168db85be5 claude
2026-05-30 19:49:37 -07:00

113 lines
3.2 KiB
TypeScript

import type Anthropic from "@anthropic-ai/sdk";
import type { Octokit } from "@octokit/rest";
import { composeDigest, type DigestInputs } from "./claude";
import { DIGEST_ISSUE_NUMBER, MAX_CALLS_PER_RUN, REPO } from "./config";
import { gatherDigestInputs, renderFallbackDigest } from "./digest-formatter";
import type { TriagePassResult } from "./pass1-triage";
import { reviewAllPRsAwaitingReview } from "./pr-reviewer";
export type DigestPassResult = {
markdown: string;
prReviewCallCount: number;
composedByClaude: boolean;
posted: boolean;
};
export type RunDigestArgs = {
octokit: Octokit;
anthropic: Anthropic;
triagePass: TriagePassResult;
now: Date;
dryRun: boolean;
remainingClaudeCalls: number;
};
export async function runDigestPass(
args: RunDigestArgs,
): Promise<DigestPassResult> {
const { octokit, anthropic, triagePass, now, dryRun, remainingClaudeCalls } =
args;
// Phase 2a — review every open PR awaiting maintainer review.
const { reviews, callCount: prReviewCallCount } =
await reviewAllPRsAwaitingReview(octokit, anthropic, {
maxCalls: Math.max(0, remainingClaudeCalls - 1),
});
// Gather all inputs for the digest composer.
const inputs = await gatherDigestInputs({
octokit,
now,
triagePass,
prReviews: reviews,
});
// Phase 2b — compose the digest markdown via Claude (with fallback).
let markdown: string;
let composedByClaude = false;
try {
const result = await composeDigest(anthropic, inputs);
markdown = result.value.markdown;
composedByClaude = true;
console.log(
`[claude-cron] digest composed — tokens in=${result.usage.input_tokens} out=${result.usage.output_tokens}`,
);
} catch (err) {
console.error(
`[claude-cron] digest composition failed; using deterministic fallback: ${err}`,
);
markdown = renderFallbackDigest(inputs);
}
let posted = false;
if (dryRun) {
console.log(`[claude-cron] DRY_RUN: digest (not posted):\n\n${markdown}\n`);
} else {
posted = await postDigestToTrackingIssue(octokit, markdown);
}
return { markdown, prReviewCallCount, composedByClaude, posted };
}
async function postDigestToTrackingIssue(
octokit: Octokit,
markdown: string,
): Promise<boolean> {
if (DIGEST_ISSUE_NUMBER === 0) {
console.warn(
`[claude-cron] DIGEST_ISSUE_NUMBER is unset in config.ts; logging digest instead of posting`,
);
console.log(markdown);
return false;
}
try {
const { data: tracking } = await octokit.rest.issues.get({
...REPO,
issue_number: DIGEST_ISSUE_NUMBER,
});
if (tracking.state === "closed") {
console.error(
`[claude-cron] tracking issue #${DIGEST_ISSUE_NUMBER} is closed; not posting. Reopen or update DIGEST_ISSUE_NUMBER.`,
);
return false;
}
} catch (err) {
console.error(
`[claude-cron] could not fetch tracking issue #${DIGEST_ISSUE_NUMBER}: ${err}`,
);
return false;
}
await octokit.rest.issues.createComment({
...REPO,
issue_number: DIGEST_ISSUE_NUMBER,
body: markdown,
});
console.log(
`[claude-cron] digest posted to tracking issue #${DIGEST_ISSUE_NUMBER}`,
);
return true;
}
export { DIGEST_ISSUE_NUMBER, MAX_CALLS_PER_RUN };
export type { DigestInputs };