Compare commits
No commits in common. "main" and "probablycorey/remove-claude-footer" have entirely different histories.
main
...
probablyco
|
|
@ -112,10 +112,8 @@ class PRHandler {
|
|||
}
|
||||
|
||||
static formatPrBody(pullRequest: Gitea.PullRequest, repositoryFullName: string): string {
|
||||
// Strip the "🤖 Generated with Claude Code" footer that gets appended to PR bodies
|
||||
const claudeFooter = /\n*🤖.*Claude Code[^\n]*$/
|
||||
const body = (pullRequest.body || "_empty_")
|
||||
.replace(claudeFooter, "")
|
||||
.replace(/\n*🤖 Generated with \[Claude Code\].*$/s, "")
|
||||
.trim() || "_empty_"
|
||||
let message = `
|
||||
> ### [${pullRequest.title}](<${pullRequest.html_url}>)
|
||||
|
|
|
|||
|
|
@ -1,33 +1,18 @@
|
|||
import { log } from "../log"
|
||||
import type { Gitea } from "./types"
|
||||
|
||||
const giteaUrl = "https://git.nose.space"
|
||||
|
||||
function tokenPrefix(): string {
|
||||
const token = process.env.GITEA_API_TOKEN
|
||||
if (!token) return "MISSING"
|
||||
return `${token.slice(0, 4)}...${token.slice(-4)} (${token.length} chars)`
|
||||
}
|
||||
|
||||
async function giteaFetch(url: string): Promise<Response> {
|
||||
export async function fetchPR(fullname: string, prNumber: number): Promise<Gitea.PullRequest> {
|
||||
const url = `${giteaUrl}/api/v1/repos/${fullname}/pulls/${prNumber}`
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `token ${process.env.GITEA_API_TOKEN}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const body = await response.text()
|
||||
log({ type: "gitea-api-error", status: response.status, url, body: body.slice(0, 500), tokenPrefix: tokenPrefix() })
|
||||
throw new Error(`Gitea API error: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
export async function fetchPR(fullname: string, prNumber: number): Promise<Gitea.PullRequest> {
|
||||
const url = `${giteaUrl}/api/v1/repos/${fullname}/pulls/${prNumber}`
|
||||
const response = await giteaFetch(url)
|
||||
return response.json() as Promise<Gitea.PullRequest>
|
||||
}
|
||||
|
||||
|
|
@ -35,8 +20,16 @@ export async function fetchReviewComments(
|
|||
fullname: string,
|
||||
prNumber: number
|
||||
): Promise<Gitea.ReviewComment[]> {
|
||||
// First, fetch all reviews
|
||||
const reviewsUrl = `${giteaUrl}/api/v1/repos/${fullname}/pulls/${prNumber}/reviews`
|
||||
const reviewsResponse = await giteaFetch(reviewsUrl)
|
||||
const reviewsResponse = await fetch(reviewsUrl, {
|
||||
headers: {
|
||||
Authorization: `token ${process.env.GITEA_API_TOKEN}`,
|
||||
},
|
||||
})
|
||||
if (!reviewsResponse.ok) {
|
||||
throw new Error(`Gitea API error: ${reviewsResponse.status} ${reviewsResponse.statusText}`)
|
||||
}
|
||||
|
||||
const reviews = (await reviewsResponse.json()) as Array<{
|
||||
id: number
|
||||
|
|
@ -47,12 +40,21 @@ export async function fetchReviewComments(
|
|||
comments_count: number
|
||||
}>
|
||||
|
||||
// For each review, fetch its comments
|
||||
const allComments: Gitea.ReviewComment[] = []
|
||||
for (const review of reviews) {
|
||||
if (review.comments_count === 0) continue
|
||||
|
||||
const commentsUrl = `${giteaUrl}/api/v1/repos/${fullname}/pulls/${prNumber}/reviews/${review.id}/comments`
|
||||
const commentsResponse = await giteaFetch(commentsUrl)
|
||||
const commentsResponse = await fetch(commentsUrl, {
|
||||
headers: {
|
||||
Authorization: `token ${process.env.GITEA_API_TOKEN}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!commentsResponse.ok) {
|
||||
throw new Error(`Gitea API error fetching review ${review.id} comments: ${commentsResponse.status} ${commentsResponse.statusText}`)
|
||||
}
|
||||
const comments = (await commentsResponse.json()) as Gitea.ReviewComment[]
|
||||
allComments.push(...comments)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ export type LogEvent =
|
|||
| { type: "crash-log-found" }
|
||||
| { type: "startup"; detail: string }
|
||||
| { type: "discord-ready" }
|
||||
| { type: "gitea-api-error"; status: number; url: string; body: string; tokenPrefix: string }
|
||||
| { type: "error"; error: unknown; context?: string }
|
||||
|
||||
export type StoredLogEvent = LogEvent & { ts: string }
|
||||
|
|
@ -51,17 +50,11 @@ function createLogFile(): string {
|
|||
return `${logsDir}/${timestamp}_${releaseSha}.jsonl`
|
||||
}
|
||||
|
||||
function extractTimestamp(filename: string): string {
|
||||
const stem = filename.replace(".jsonl", "")
|
||||
// New format: timestamp_sha, Old format: sha_timestamp
|
||||
if (/^\d{4}-/.test(stem)) return stem.split("_").slice(0, -1).join("_")
|
||||
return stem.split("_").pop() || ""
|
||||
}
|
||||
|
||||
export function listLogFiles(): string[] {
|
||||
return readdirSync(logsDir)
|
||||
.filter((f) => f.endsWith(".jsonl"))
|
||||
.sort((a, b) => extractTimestamp(b).localeCompare(extractTimestamp(a)))
|
||||
.sort()
|
||||
.reverse()
|
||||
.slice(0, 20)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,32 +108,11 @@ function EventRow({ event, index }: { event: StoredLogEvent; index: number }) {
|
|||
)
|
||||
}
|
||||
|
||||
function parseLogFilename(file: string): { sha: string; isoStr: string } {
|
||||
const stem = file.replace(".jsonl", "")
|
||||
// New format: timestamp_sha (e.g. 2026-03-10T18-06-23-561Z_cb10121)
|
||||
// Old format: sha_timestamp (e.g. cb10121_2026-03-10T18-06-23-561Z)
|
||||
const isNewFormat = /^\d{4}-/.test(stem)
|
||||
const parts = stem.split("_")
|
||||
|
||||
let sha: string
|
||||
let rawTimestamp: string
|
||||
if (isNewFormat) {
|
||||
sha = parts.pop() || "unknown"
|
||||
rawTimestamp = parts.join("_")
|
||||
} else {
|
||||
rawTimestamp = parts.pop() || ""
|
||||
sha = parts.join("_") || "unknown"
|
||||
}
|
||||
|
||||
const isoStr = rawTimestamp.replace(/T(\d{2})-(\d{2})-(\d{2})-(\d+)Z/, "T$1:$2:$3.$4Z")
|
||||
return { sha, isoStr }
|
||||
}
|
||||
|
||||
function Sidebar({ files, selectedFile }: { files: string[]; selectedFile?: string }) {
|
||||
// Group files by sha, sorted by most recent file in each group
|
||||
const grouped: Record<string, string[]> = {}
|
||||
for (const file of files) {
|
||||
const { sha } = parseLogFilename(file)
|
||||
const sha = file.replace(".jsonl", "").split("_").pop() || "unknown"
|
||||
const list = grouped[sha] || (grouped[sha] = [])
|
||||
list.push(file)
|
||||
}
|
||||
|
|
@ -151,7 +130,8 @@ function Sidebar({ files, selectedFile }: { files: string[]; selectedFile?: stri
|
|||
<div style="padding: 4px 16px; color: #6b7280; font-size: 11px; font-family: monospace">{sha}</div>
|
||||
{shaFiles.map((file) => {
|
||||
const isSelected = file === selectedFile
|
||||
const { isoStr } = parseLogFilename(file)
|
||||
// Parse ISO timestamp back from filename: 2026-03-10T02-56-59-938Z_sha.jsonl
|
||||
const isoStr = file.replace(/_[^_]+\.jsonl$/, "").replace(/T(\d{2})-(\d{2})-(\d{2})-(\d+)Z/, "T$1:$2:$3.$4Z")
|
||||
const bg = isSelected ? "#2a2a2a" : "transparent"
|
||||
return (
|
||||
<a
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user