Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
475 changes: 475 additions & 0 deletions .codeflash/standups/app.py

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions .codeflash/standups/generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""Pull GitHub PR data and standup notes into data.json for the Dash app."""

import json
import os
import re
from datetime import datetime, timedelta, timezone
from pathlib import Path

import requests

REPOS = ["codeflash", "codeflash-internal", "codeflash-agent", "github-workflows"]
ORG = "codeflash-ai"
NOTES_DIR = Path(__file__).parent / "notes"
DATA_FILE = Path(__file__).parent / "data.json"
CI_AUDIT_FILE = Path(__file__).parents[2] / ".." / "codeflash-agent" / "reports" / "codeflash-ci-audit" / "data.json"


def gh_token() -> str:
token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN", "")
if not token:
import subprocess

result = subprocess.run(["gh", "auth", "token"], capture_output=True, text=True, check=False)
token = result.stdout.strip()
return token


def gh_headers() -> dict[str, str]:
return {"Authorization": f"token {gh_token()}", "Accept": "application/vnd.github+json"}


def fetch_prs(state: str, since: datetime | None = None) -> list[dict]:
prs = []
for repo in REPOS:
url = f"https://api.github.com/repos/{ORG}/{repo}/pulls"
params: dict[str, str] = {"state": state, "per_page": "30", "sort": "updated", "direction": "desc"}
resp = requests.get(url, headers=gh_headers(), params=params, timeout=15)
if resp.status_code != 200:
continue
for pr in resp.json():
updated = datetime.fromisoformat(pr["updated_at"].replace("Z", "+00:00"))
if since and updated < since:
continue
prs.append(
{
"repo": repo,
"number": pr["number"],
"title": pr["title"],
"state": pr["state"],
"author": pr["user"]["login"],
"url": pr["html_url"],
"created_at": pr["created_at"],
"updated_at": pr["updated_at"],
"merged_at": pr.get("merged_at"),
"draft": pr.get("draft", False),
}
)
return prs


def parse_note(path: Path) -> dict:
text = path.read_text(encoding="utf-8")
sections: dict[str, list[str]] = {}
current = None
title = None
for line in text.splitlines():
h1 = re.match(r"^#\s+(.+)", line)
if h1 and not title:
title = h1.group(1).strip()
continue
heading = re.match(r"^##\s+(.+)", line)
if heading:
current = heading.group(1).strip().lower()
sections[current] = []
elif current and line.strip().startswith("- "):
sections[current].append(line.strip().removeprefix("- "))
return {"date": path.stem, "title": title or path.stem, "sections": sections}


def load_notes() -> list[dict]:
if not NOTES_DIR.exists():
return []
notes = []
for f in sorted(NOTES_DIR.glob("*.md"), reverse=True):
notes.append(parse_note(f))
return notes


def main():
now = datetime.now(timezone.utc)
week_ago = now - timedelta(days=7)

open_prs = fetch_prs("open")
closed_prs = fetch_prs("closed", since=week_ago)
merged_prs = [pr for pr in closed_prs if pr["merged_at"]]
notes = load_notes()

ci_audit = None
resolved = CI_AUDIT_FILE.resolve()
if resolved.exists():
ci_audit = json.loads(resolved.read_text(encoding="utf-8"))

data = {
"generated_at": now.isoformat(),
"org": ORG,
"repos": REPOS,
"open_prs": open_prs,
"merged_prs": merged_prs,
"notes": notes,
"summary": {
"total_open": len(open_prs),
"total_merged_7d": len(merged_prs),
"draft_count": sum(1 for pr in open_prs if pr["draft"]),
"repos_with_open_prs": len({pr["repo"] for pr in open_prs}),
},
"ci_audit": ci_audit,
}

DATA_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
print(f"Wrote {DATA_FILE} ({len(open_prs)} open, {len(merged_prs)} merged)")


if __name__ == "__main__":
main()
30 changes: 30 additions & 0 deletions .codeflash/standups/notes/2026-04-23.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Kevin — 2026-04-23

## Done

- CI audit across codeflash org: disabled Actions on 200+ forks, built interactive Dash cost report showing ~$12K/yr savings
- Scaffolded codeflash-api FastAPI rewrite (all 9 endpoints), unified sync/async code paths, set up Postgres integration tests with testcontainers, 78% coverage
- Project governance: enabled GitHub Discussions, created parent issue #2079 for 100% test coverage, filed coverage issue on typeagent-py
- Merged 5 PRs on codeflash: PR template + linked-issue policy (#2093), coverage CI with 58% floor (#2094), CODEOWNERS (#2095), 2 dependabot bumps
- Triaged ~28 open PRs, posted contributor follow-ups, closed stale PRs and reopened as issues
- Reimplemented JS function tracer from stale PR #1377 onto current main as PR #2105
- Created PRs #244 (tests for resolve_azure_model_name) and #245 (max_retries bump) on typeagent-py
- Bumped stale action versions across 3 repos (setup-uv, prek-action, gh-action-pypi-publish)
- Fixed 3-month-old VSCode extension build failure from out-of-sync package-lock.json (PR #2617)
- Extended shared ci-python-uv.yml with test-secret-env input, migrated aiservice-test job to use it (PR #2620)
- Initialized tessl (vendored mode) across codeflash, codeflash-internal, and codeflash-agent with weekly tile update workflows
- Created codeflash-ci-bot GitHub App for PR creation (enterprise GITHUB_TOKEN workaround)
- Added org-level ruleset requiring PR branches to be up to date with main
- Ran vulture dead-code audit (428/432 false positives), deleted 4 verified dead items, added vulture as dev dep
- Cleaned up 19 stale local branches and removed associated worktrees

## Blocked

- Need more perms across the org

## Next

- Missing tessl tiles (~20 on internal, ~13 on agent) will resolve as tessl publishes them
- Unpin claude-code-action once Bedrock OIDC regression is fixed
- Re-enable Dependabot to address 24 known vulnerabilities
- Improve comparator coverage (52%, identified as biggest gap)
12 changes: 12 additions & 0 deletions .codeflash/standups/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[project]
name = "codeflash-standups"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"dash[cloud]>=4.1",
"plotly>=6.7",
"requests>=2.31",
]

[project.scripts]
standup = "generate:main"
63 changes: 63 additions & 0 deletions .codeflash/standups/theme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Theme and styling constants for the standup report."""

# ── Colors (Codeflash dark - amber/zinc) ────────────────────────────────────
ACCENT = "#ffd227"
DARK = "#09090b"
CARD_BG = "rgba(16,20,28,0.7)"
CARD_BORDER = "rgba(63,63,70,0.35)"
SLATE = "#e4e4e7"
GRAY = "#a1a1aa"
LIGHT_GRAY = "#71717a"
BG = "#0d1117"
WHITE = "#fafafa"
GREEN = "#4ade80"
LIGHT_GREEN = "rgba(74,222,128,0.12)"
RED = "#f87171"
LIGHT_RED = "rgba(248,113,113,0.12)"
AMBER = "#fbbf24"
BLUE = "#60a5fa"
PURPLE = "#a78bfa"

GRID_BG_IMAGE = (
"linear-gradient(to right, currentColor 1px, transparent 1px),"
"linear-gradient(to bottom, currentColor 1px, transparent 1px)"
)
GRID_BG_SIZE = "48px 48px"
GRID_OVERLAY = {
"position": "fixed",
"top": 0,
"left": 0,
"right": 0,
"bottom": 0,
"backgroundImage": GRID_BG_IMAGE,
"backgroundSize": GRID_BG_SIZE,
"opacity": "0.05",
"pointerEvents": "none",
"zIndex": "0",
}

CARD = {"background": CARD_BG, "borderRadius": "16px", "padding": "28px 32px", "border": f"1px solid {CARD_BORDER}"}
FONT = "'Inter', system-ui, -apple-system, sans-serif"
MONO = "'JetBrains Mono', 'Menlo', monospace"

TABLE_HEADER: dict[str, str] = {
"backgroundColor": "rgba(24,24,27,0.8)",
"color": ACCENT,
"fontWeight": "600",
"fontSize": "13px",
"padding": "12px 16px",
"borderBottom": f"1px solid {CARD_BORDER}",
}
TABLE_CELL: dict[str, str] = {
"textAlign": "left",
"padding": "12px 16px",
"fontSize": "13px",
"fontFamily": FONT,
"border": "none",
"color": SLATE,
}
TABLE_DATA: dict[str, str] = {"backgroundColor": "rgba(24,24,27,0.5)", "color": SLATE}
TABLE_DATA_CONDITIONAL: list[dict[str, object]] = [
{"if": {"row_index": "odd"}, "backgroundColor": "rgba(31,31,35,0.6)"}
]
TABLE_WRAP: dict[str, str] = {"borderRadius": "16px", "overflow": "hidden", "border": f"1px solid {CARD_BORDER}"}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -428,4 +428,5 @@ replay_pid*
code_to_optimize/**/package-lock.json

# Other tools
.codeflash/
.codeflash/*
!.codeflash/standups/
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ repos:
rev: v0.15.8
hooks:
- id: ruff-check
exclude: ^\.codeflash/standups/
- id: ruff-format
exclude: ^\.codeflash/standups/

- repo: local
hooks:
Expand All @@ -13,3 +15,4 @@ repos:
language: system
types: [python]
require_serial: true
exclude: ^\.codeflash/standups/
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ warn_unreachable = true
install_types = true
plugins = ["pydantic.mypy"]

exclude = ["tests/", "code_to_optimize/", "pie_test_set/", "experiments/"]
exclude = ["tests/", "code_to_optimize/", "pie_test_set/", "experiments/", "\\.codeflash/standups/"]

[[tool.mypy.overrides]]
module = ["jedi", "jedi.api.classes", "inquirer", "inquirer.themes", "numba"]
Expand All @@ -233,7 +233,7 @@ target-version = "py39"
line-length = 120
fix = true
show-fixes = true
extend-exclude = ["code_to_optimize/", "pie_test_set/", "tests/", "experiments/"]
extend-exclude = ["code_to_optimize/", "pie_test_set/", "tests/", "experiments/", ".codeflash/standups/"]

[tool.ruff.lint]
select = ["ALL"]
Expand Down
Loading