Skip to content

feat(ui): add vampire coder index metric (fixes #44)#281

Open
Diwakar-odds wants to merge 1 commit into
bitflicker64:mainfrom
Diwakar-odds:feat/issue-44-vampire
Open

feat(ui): add vampire coder index metric (fixes #44)#281
Diwakar-odds wants to merge 1 commit into
bitflicker64:mainfrom
Diwakar-odds:feat/issue-44-vampire

Conversation

@Diwakar-odds

Copy link
Copy Markdown
Contributor

Summary

Introduces the Vampire Coder Index metric for user behavior analysis.

Motivation

Closes #44.

Changes

  • termstory/insights.py:
    • [MODIFY] Added calculate_vampire_index to compute the ratio of late-night (00:00-05:00) sessions.
  • termstory/tui.py:
    • [MODIFY] Updated the headers in Timeframe, Wrapped, and Daily Chronicle views to display the new metric.

Acceptance Criteria

  • Implemented Vampire Coder Index tracker.
  • Displayed correctly in the TUI terminal interface headers.

Impact & Side Effects

No breaking changes. Adds visual metrics to the TUI.

How to Test

  1. Run termstory gui and open any dashboard timeframe view to verify the headers.
  2. The UI headers should render the VAMPIRE INDEX metric correctly.

Quality Checklist

  • Code aligns with PEP-8.
  • No temporary or scratch files are included in the commit.
  • Commit messages follow conventional format.

@git-miku git-miku Bot added the needs-triage Awaiting human triage — run /triage to mark as triaged label Jul 14, 2026
@git-miku git-miku Bot added size/S PR changes ~38 lines area/ai area/ai area/tui area/tui labels Jul 14, 2026
@git-miku

git-miku Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Tip

👋 Hey @Diwakar-odds — Miku's on it. Here are the most useful commands for this PR:

💬 Command What it does
:miku /review 🔍 Line-by-line review
:miku /review-sc ✏️ Inline suggested changes
:miku /security 🛡️ Security scan
:miku /perf ⚡ Performance notes
:miku /test 🧪 Test suggestions
:miku /summarize 📝 TL;DR

Note

🎓 This repo is part of ECSOC26. Run :miku /ecsoc to add the ECSOC26 label to this PR.

Note

⭐ Star this repo to unlock all commands. Say :miku /help for the full list.

📖 All commands
🤖 AI-powered — 14 commands
Command Description Works on
:miku /ask <question> Answer a question about the codebase Issues & PRs
:miku /review Full code review (auto-fetches diff) PRs only
:miku /review-sc Post inline suggested-change comments PRs only
:miku /security OWASP security review (injection, auth, crypto…) PRs only
:miku /perf Performance & efficiency analysis PRs only
:miku /docs Generate docstrings for changed code PRs only
:miku /test Suggest unit tests for changed code PRs only
:miku /roast Humorous but constructive code roast 🔥 PRs only
:miku /checklist Smart diff-aware PR checklist PRs only
:miku /summarize TL;DR of this issue or PR Issues & PRs
:miku /explain <target> Explain a file, function, or concept Issues & PRs
:miku /changelog Draft a CHANGELOG entry PRs only
:miku /estimate Story-point effort estimate Issues only
:miku /chat <message> General conversation Issues & PRs
🔧 Issue & PR management — 18 commands
Command Description Works on
:miku /assign [@user] Assign to yourself or another user Issues & PRs
:miku /unassign [@user] Remove yourself or another user Issues & PRs
:miku /label <name> Add a label Issues & PRs
:miku /remove-label <name> Remove a label Issues & PRs
:miku /close [reason] Close this issue or PR Issues & PRs
:miku /reopen Reopen a closed issue or PR Issues & PRs
:miku /approve [message] Submit an approving review on a PR PRs only
:miku /retitle <new title> Change the title Issues & PRs
:miku /duplicate #N Mark as duplicate of #N and close Issues & PRs
:miku /milestone [name] Set or list open milestones Issues & PRs
:miku /ship [version] Mark PR as shipped / deployed PRs only
:miku /lock [reason] Lock conversation (resolved / off-topic / spam) Issues & PRs
:miku /unlock Unlock conversation Issues & PRs
:miku /cc @user1 [@user2] Request reviews PRs only
:miku /lgtm [cancel] Signal "Looks Good To Me" Issues & PRs
:miku /hold [cancel] Block merge; :miku /hold cancel releases it Issues & PRs
:miku /wip Toggle WIP (converts PR to draft) Issues & PRs
:miku /merge Merge this PR (CI + approval required) PRs only
🎉 Community & utility — 9 commands
Command Description Works on
:miku /vote Open a 👍 👎 🚀 reaction poll Issues & PRs
:miku /kudos @user [msg] Give public kudos to a contributor Issues & PRs
:miku /stats Repo stars, forks, top contributors Issues & PRs
:miku /remind <dur> <msg> Schedule a reminder (e.g. 2h, 30m, 1d) Issues & PRs
:miku /size Show PR size breakdown PRs only
:miku /ping [@user ...] Ping contributors with an AI message Issues & PRs
:miku /joke Tell a programming joke Issues & PRs
:miku /shrug Add ¯_(ツ)_/¯ label Issues & PRs
:miku /help Show this message Issues & PRs

@Diwakar-odds

Copy link
Copy Markdown
Contributor Author

PR Analysis & Label Request

This PR introduces the Vampire Coder Index (Issue #44) to the TUI dashboards. It calculates the user's tendency for late-night coding sessions and seamlessly integrates this visual metric across all three main dashboard components.

ECSoC26 Label Justification:
Based on the code changes and ECSoC criteria, I kindly request this PR be labeled with:

  • \ECSoC26-L2\ as this introduces significant UI features and analytics rendering.
  • \good-ui\ and \good-issue\ as this implements highly requested visual gamification elements and complex data grouping.

Please review when you have a moment. Thank you!

@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Vampire Coder Index metric to the TUI. The main changes are:

  • A new session-level late-night percentage helper in termstory/insights.py.
  • The new metric imported into termstory/tui.py.
  • Vampire Index header lines added to timeframe, wrapped, and daily chronicle views.

Confidence Score: 4/5

The TUI header rendering path needs a markup fix before merging.

  • The new metric helper is small and follows existing timestamp handling.
  • The added header strings can be parsed as malformed Rich markup in three reachable views.
  • Removing the extra close tag keeps the new display behavior without changing the metric logic.

termstory/tui.py

Important Files Changed

Filename Overview
termstory/insights.py Adds calculate_vampire_index, using the same local timestamp convention as nearby insight helpers.
termstory/tui.py Displays the new metric in three header renderers, but the added Rich markup has unmatched close tags.

Reviews (1): Last reviewed commit: "feat(ui): add vampire coder index metric..." | Re-trigger Greptile

Comment thread termstory/tui.py
@@ -929,7 +931,7 @@ def render_time_summary(
header_lines.append(f"[bold cyan]{avatar_lines[6]}[/] [bold cyan]ACTIVE REPOS:[/] [bold]{active_projects_count} Workspaces[/]")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unmatched Rich Close Tag

When the timeframe header is rendered, [/dim] closes the dim span and the trailing [/] has no matching open tag. Rich can reject this markup, so opening the timeframe dashboard can fail instead of showing the new metric.

Comment thread termstory/tui.py
@@ -1160,7 +1164,7 @@ def _render_wrapped_view_ui(
header_lines.append(f"[bold cyan]{avatar_lines[6]}[/] [bold cyan]ACTIVE REPOS:[/] [bold]{active_projects_count} Workspaces[/]")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unmatched Rich Close Tag

When the wrapped view header is rendered, the new VAMPIRE INDEX line closes [dim] with [/dim] and then closes another style with [/]. That extra close tag can make the wrapped view fail while parsing the header markup.

Comment thread termstory/tui.py
@@ -1357,7 +1363,7 @@ def render_daily_chronicle_view(self, date_str: str, sessions: List[Session], pr
header_lines.append(f"[bold cyan]{avatar_lines[6]}[/] [bold cyan]ACTIVE SESSIONS:[/] [bold]{len(sessions)}[/]")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unmatched Rich Close Tag

When the daily chronicle header is rendered, this new line has an extra generic close tag after [/dim]. Rich can treat that as malformed markup, so the daily chronicle view can fail before displaying the header.

@bitflicker64 bitflicker64 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Diwakar-odds, this is the third PR in the trio (with #282 and #280) and it shares two of the same blockers plus a new one. Needs another pass before merge.

What's blocking

  1. CI is red on all four Python versions (MarkupError: auto closing tag ('[/]') has nothing to close in test_tui_landing_page_after_onboarding). Same root cause as PR #280: the f-string at termstory/tui.py:934 ends with ...[/dim][/] — extra trailing [/] because the wrapping pattern from the DAILY CLASS header in #282 was reused without adjusting. Compare to the line it replaces (PROJECTS: row) which balances cleanly with one [/] at the end. Three sites have the bug (934, 1167, 1366); Greptile flagged all three as P1s. Fix is the same in all three places.

  2. Duplicate logic with calculate_vampire_coder_index. A calculate_vampire_coder_index(sessions) -> float already exists on main at termstory/insights.py:446, plus a richer get_vampire_metrics(sessions) that powers format_vampire_index in formatter.py and the vampire rendering in cli.py. Same situation as PR #280's necromancer duplicate.

  3. The two implementations disagree on what's being measured. This is worse than the necromancer duplicate. The new calculate_vampire_index counts whole sessions whose start_time falls in [00:00, 05:00). The existing calculate_vampire_coder_index counts each command and commit individually in that window, deduplicating commits by hash. These produce different numbers for the same data — e.g. a 6-hour session starting at 4 AM contributes 1 to the new one but could contribute hundreds of commands to the existing one. The issue text says "percentage and intensity of your coding sessions" which is closer to the new helper's definition, but the formatter/cli path renders the existing one. Two metrics with the same name will confuse users and split the codebase.

Suggested fix shape

  • Pick one definition. If sessions-by-start-time matches the issue intent, replace the existing calculate_vampire_coder_index with this new helper, migrate formatter.py and cli.py, and delete the old one. If commands-and-commits is the canonical metric (which is what the codebase already has wired), delete the new helper and call get_vampire_metrics(sessions).get("vampire_index", 0.0) in the three header sites.
  • Fix the trailing [/] in all three header lines (drop the [/] after [/dim]).
  • Add at minimum a unit test that constructs sessions with known start times and asserts the percentage.

Smaller notes

  • No is_legacy filter, same drift as the existing implementations.
  • Empty-session returns 0.0 (float), so the header will render 0.0% late night. Slightly cleaner if you return 0 for empty, or format as {vampire_index:g}% to drop trailing .0.
  • VAMPIRE INDEX: is 14 chars while most other labels in the column are 9 (PROJECTS:, COMMITS:). The extra spaces compensate but the row drifts if you ever shorten the label. Minor.
  • Wrapped header at tui.py:1167 replaces COMMITS: with VAMPIRE INDEX:, so monthly wrapped summaries lose the commit-volume metric. total_commits is still computed above but no longer rendered. Same regression as PR #282 and #280. Add a row, don't replace.

Happy to approve once one of the two helpers is removed, the markup is fixed, and CI is green.

Comment thread termstory/tui.py
header_lines.append(f"[bold cyan]{avatar_lines[7]}[/] [bold cyan]FOCUS SCORE:[/] [bold green]{fs:.1f}/10.0[/]")
header_lines.append(f"[bold cyan]{avatar_lines[8]}[/] [bold cyan]PEAK VELOCITY:[/] [dim]{peak_velocity}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]PROJECTS:[/] [dim]{active_projects_count}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim][/]")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Root cause of the CI failure on all 4 Python versions. The f-string ends with ...[/dim][/] — extra trailing [/]. Fix: drop the [/] after [/dim], leaving [dim]{vampire_index}% late night[/dim]. Compare to the line it replaces (PROJECTS:[/] ... [dim]{active_projects_count}[/]) which balances cleanly.

Comment thread termstory/tui.py
header_lines.append(f"[bold cyan]{avatar_lines[7]}[/] [bold cyan]FOCUS SCORE:[/] [bold green]{fs:.1f}/10.0[/]")
header_lines.append(f"[bold cyan]{avatar_lines[8]}[/] [bold cyan]PEAK VELOCITY:[/] [dim]{peak_velocity}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]COMMITS:[/] [dim]{total_commits}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim][/]")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same extra [/] markup bug as line 934. Fix mirrors that line.

Comment thread termstory/tui.py
header_lines.append(f"[bold cyan]{avatar_lines[7]}[/] [bold cyan]FOCUS SCORE:[/] [bold green]{fs:.1f}/10.0[/]")
header_lines.append(f"[bold cyan]{avatar_lines[8]}[/] [bold cyan]PEAK TIME:[/] [dim]{peak_velocity}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]PROJECTS:[/] [dim]{len(projects)}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim][/]")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same extra [/] markup bug as line 934. Fix mirrors that line.

Comment thread termstory/tui.py
header_lines.append(f"[bold cyan]{avatar_lines[7]}[/] [bold cyan]FOCUS SCORE:[/] [bold green]{fs:.1f}/10.0[/]")
header_lines.append(f"[bold cyan]{avatar_lines[8]}[/] [bold cyan]PEAK VELOCITY:[/] [dim]{peak_velocity}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]COMMITS:[/] [dim]{total_commits}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim][/]")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same regression as PR #282 and #280 — wrapped view loses total_commits because the header row is replaced instead of added. total_commits is still computed above this line. Add a second header_lines.append for the commit count instead of swapping.

@bitflicker64 bitflicker64 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Request changes — CI red on all Python versions; also duplicates an existing vampire metric API.

Intent

Show a “Vampire Index” (% late-night work) in TUI headers (issue #44).

Critical

  1. Broken Rich markup (CI failure) — every header line ends with [/dim][/]:

    [dim]{vampire_index}% late night[/dim][/]

    The trailing [/] has nothing to close → MarkupError in test_tui_landing_page_after_onboarding on 3.9–3.12.

    Fix:

    [dim]{vampire_index}% late night[/dim]
  2. Duplicate metricmain already has calculate_vampire_coder_index / get_vampire_metrics with a richer definition (commands + commits). This PR adds a simpler session-start-hour-only calculate_vampire_index. Prefer reusing the existing helper so CLI/TUI/analyze_all stay consistent.

Warnings

  • Replacing the PROJECTS / COMMITS header row drops useful info in those views; add a row instead of overwriting.
  • No unit tests for the calculation edge cases (empty list, exactly midnight, hour==5 boundary).
  • Local timezone via datetime.fromtimestamp — fine if intentional; document that it’s local wall clock.

Verdict

Request changes — fix markup first (one-character class fix), then align with existing vampire APIs.

Comment thread termstory/tui.py
header_lines.append(f"[bold cyan]{avatar_lines[7]}[/] [bold cyan]FOCUS SCORE:[/] [bold green]{fs:.1f}/10.0[/]")
header_lines.append(f"[bold cyan]{avatar_lines[8]}[/] [bold cyan]PEAK VELOCITY:[/] [dim]{peak_velocity}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]PROJECTS:[/] [dim]{active_projects_count}[/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim][/]")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical / CI red: Extra closing tag.

Suggested change
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim][/]")
header_lines.append(f"[bold cyan]{avatar_lines[9]}[/] [bold cyan]VAMPIRE INDEX:[/] [dim]{vampire_index}% late night[/dim]")

Apply the same one-line fix at the wrapped-view and daily-chronicle sites.

Comment thread termstory/insights.py
break
return streak

def calculate_vampire_index(sessions: List[Session]) -> float:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main already ships calculate_vampire_coder_index / get_vampire_metrics. A second, simpler definition will drift. Prefer:

from termstory.insights import calculate_vampire_coder_index
vampire_index = calculate_vampire_coder_index(sessions)

…or explicitly justify why the TUI needs a different formula, and share one implementation.

@bitflicker64

Copy link
Copy Markdown
Owner

/triage

@git-miku git-miku Bot added triage/triaged This PR has been triaged by a maintainer and removed needs-triage Awaiting human triage — run /triage to mark as triaged labels Jul 18, 2026
@git-miku

git-miku Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

miku /triage

✅ Marked as triaged (triage/triaged applied, needs-triage removed).

@bitflicker64

Copy link
Copy Markdown
Owner

:miku /check-star

@git-miku git-miku Bot added the do-not-merge/needs-star PR author has not starred this repo — run /check-star after starring label Jul 18, 2026
@git-miku

git-miku Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

miku /check-star

@Diwakar-odds still hasn't starred this repo. Applied do-not-merge/needs-star.

Star this repo first, then run :miku /check-star again. ⭐

@bitflicker64

Copy link
Copy Markdown
Owner

@gemini

@bitflicker64

Copy link
Copy Markdown
Owner

@gemini review

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ai area/ai area/tui area/tui do-not-merge/needs-star PR author has not starred this repo — run /check-star after starring size/S PR changes ~38 lines triage/triaged This PR has been triaged by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The Vampire Coder Index 🧛‍♂️

2 participants