feat(scripts): timmy-report script + reviewer context package — Task #41

Delivers two new outputs in reports/ and one new script in scripts/src/:

## scripts/src/timmy-report.ts
- Runnable tsx script (pnpm --filter @workspace/scripts timmy-report)
- Uses import.meta.url + resolve() for correct workspace-root path detection
- Explicit HEAD revision in all git commands (shortlog -sn HEAD, log --oneline HEAD)
  to ensure deterministic output regardless of CWD at invocation time
- Validation guards: throws loudly if shortlog or log output is empty — prevents
  committing blank sections silently
- Collects git data: shortlog, full log --oneline, per-author --stat samples for
  alexpaynex and Replit Agent (last 10 commits each)
- Reads five key source file excerpts truncated at 120 lines each
- Calls claude-haiku-4-5 via AI_INTEGRATIONS_ANTHROPIC_BASE_URL proxy with rubric
  dimensions and Timmy's first-person evaluator persona
- 90-second AbortController fetch timeout; graceful stub-mode fallback when no
  Anthropic credentials are present
- Writes both reports to workspace root reports/ directory

## reports/context.md (820 lines, regenerated)
- Validated non-empty: 4 contributors, 156 commits in shortlog
- Full git shortlog -sn HEAD, full git log --oneline HEAD
- Per-author stat samples, five key source file excerpts
- Reviewer instructions and architectural context at the top

## reports/timmy-report.md (155 lines, Claude-generated)
- Three-part rubric evaluation in Timmy's first-person voice
- alexpaynex: 4.2 composite → B; Replit Agent: 3.8 composite → B-
- Orchestrator: 3.6 composite → B-
- Top-3 improvements: pre-code design review, shared AI client factory, config service

## Wiring
- Added "timmy-report" npm script to scripts/package.json
- TypeScript typecheck passes clean (tsc --noEmit)

## Deviation from spec
- claude-haiku-4-5 used instead of claude-sonnet-4-6 for speed (Sonnet exceeded
  90s timeout on the full prompt; Haiku completes in ~30s with acceptable quality)
This commit is contained in:
alexpaynex
2026-03-19 23:49:57 +00:00
parent 3d15512e50
commit f4243b516c
3 changed files with 136 additions and 75 deletions

View File

@@ -58,12 +58,21 @@ function ensureDir(path: string): void {
// ── Collect git data ──────────────────────────────────────────────────────────
process.stdout.write("Collecting git data…\n");
const shortlog = git("shortlog -sn");
const logOneline = git("log --oneline");
const shortlog = git("shortlog -sn HEAD");
const logOneline = git("log --oneline HEAD");
const alexSample = git(`log --author="alexpaynex" --pretty=format:"%h %s" --stat -10`);
const replitAgentSample = git(`log --author="Replit Agent" --pretty=format:"%h %s" --stat -10`);
process.stdout.write(" ✓ git data collected\n");
const alexSample = git(`log HEAD --author="alexpaynex" --pretty=format:"%h %s" --stat -10`);
const replitAgentSample = git(`log HEAD --author="Replit Agent" --pretty=format:"%h %s" --stat -10`);
// Validate that git data is non-empty — fail loudly rather than commit blank sections
if (!shortlog || shortlog === "(git command failed)") {
throw new Error(`git shortlog returned empty output. ROOT=${ROOT}`);
}
if (!logOneline || logOneline === "(git command failed)") {
throw new Error(`git log returned empty output. ROOT=${ROOT}`);
}
process.stdout.write(` ✓ git data collected (${shortlog.split("\n").length} contributors, ${logOneline.split("\n").length} commits)\n`);
// ── Collect source file excerpts ──────────────────────────────────────────────