Skip to content

feat(ui): add rpg class assigner metric (fixes #38)#282

Merged
bitflicker64 merged 6 commits into
bitflicker64:mainfrom
Diwakar-odds:feat/issue-38-rpg
Jul 21, 2026
Merged

feat(ui): add rpg class assigner metric (fixes #38)#282
bitflicker64 merged 6 commits into
bitflicker64:mainfrom
Diwakar-odds:feat/issue-38-rpg

Conversation

@Diwakar-odds

@Diwakar-odds Diwakar-odds commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a daily RPG class metric that analyzes command frequency to assign developer archetypes (e.g., "Level 12 Docker Demolitionist") for TUI dashboard gamification. This replaces the previous dictionary-based assign_rpg_class with a simpler string-returning assign_daily_rpg_class function and integrates the metric into Wrapped and Daily Chronicle view headers.

Changes

  • Core (termstory/insights.py):
    • Added assign_daily_rpg_class() function that returns a formatted string (e.g., "Level 12 Docker Demolitionist") instead of a dictionary
    • Updated analyze_all() to call the new function and extract the class string directly
  • TUI (termstory/tui.py):
    • Updated imports and calls in calculate_dashboard_stats() to use assign_daily_rpg_class
    • Added "DAILY CLASS" metric to Wrapped view header in _render_wrapped_view_ui()
    • Added "DAILY CLASS" metric to Daily Chronicle view header in render_daily_chronicle_view()
    • Added traceback import in refresh_details_canvas() for debugging
  • Tests (tests/test_insights.py):
    • Added test_assign_daily_rpg_class() covering empty sessions, known commands (kubectl), and fallback for unknown commands

Architecture / Flow

flowchart LR
    A[Session Commands] --> B[assign_daily_rpg_class]
    B --> C[Command Frequency Count]
    C --> D[Class Map Lookup]
    D --> E[Level Calculation]
    E --> F[Formatted String Output]
    F --> G[TUI Headers]
    F --> H[analyze_all]
Loading

Fixes

Testing

Run pytest tests/test_insights.py::test_assign_daily_rpg_class to verify the new function handles empty sessions, known commands, and fallback scenarios. To test the TUI integration, run termstory gui and navigate to any Wrapped or Daily Chronicle view to confirm the "DAILY CLASS" metric renders correctly in the header.

Notes

The new assign_daily_rpg_class() function returns a string directly, unlike the previous assign_rpg_class() which returned a dictionary with a "class_name" key. This is an internal API change but maintains backward compatibility in the analyze_all() return value. The function includes special handling for piped commands (defaults to "grep" for Regex Sorcerer logic) and strips basename from command paths using os.path.basename().

@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 RPG Class Assigner (Issue #38) to the TUI dashboards. It calculates the user's primary command usage and dynamically assigns them an RPG developer archetype (e.g. "Docker Demolitionist").

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 an RPG-style class metric to the app. The main changes are:

  • New daily RPG class assignment in termstory/insights.py.
  • TUI headers updated to display the class string in the relevant views.
  • Insight tests added for empty input, known commands, and fallback command names.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
termstory/insights.py Adds a separate string-returning daily RPG class helper while keeping the existing aggregate class helper contract.
termstory/tui.py Displays the daily RPG class string in the TUI and keeps commit totals based on session commits.
tests/test_insights.py Adds focused coverage for the new daily RPG class assignment helper.

Reviews (4): Last reviewed commit: "Merge remote-tracking branch 'upstream/m..." | Re-trigger Greptile

Comment thread termstory/insights.py Outdated
Comment thread termstory/tui.py
Comment thread termstory/tui.py

@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.

@/tmp/pr282-review-body.md

@bitflicker64
bitflicker64 dismissed their stale review July 14, 2026 10:22

Superseded by a corrected review

bitflicker64

This comment was marked as off-topic.

bitflicker64

This comment was marked as off-topic.

bitflicker64

This comment was marked as off-topic.

bitflicker64

This comment was marked as off-topic.

bitflicker64

This comment was marked as off-topic.

@bitflicker64
bitflicker64 dismissed their stale review July 14, 2026 10:24

Superseded by review with inline comments

@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, thanks for picking up #38. The idea is fun and the helper logic is mostly sound, but I hit a real P1 while reading and CI is red on all four Python versions, so this needs another pass before merge.

What's blocking

  1. Duplicate assign_rpg_class in termstory/insights.py. Your new string-returning version lives at line 209. There's already a dict-returning assign_rpg_class at line 548 that the codebase depends on. In Python the last def wins, so every from termstory.insights import assign_rpg_class resolves to the dict version. That means your new helper is dead code on import, and the three header call sites in tui.py are actually receiving a dict, not a string.

  2. That's why CI is red. MarkupError: auto closing tag ('[/]') has nothing to close in test_tui_landing_page_after_onboarding comes from the new DAILY CLASS header interpolating a dict repr ({'class_name': 'Regex Sorcerer', ...}) into a Rich markup string. The unescaped [ from the dict breaks the parser. Same pattern at all three header sites.

  3. Wrapped view loses the commit count. In _render_wrapped_view_ui you replaced [bold cyan]COMMITS:[/] [dim]{total_commits}[/] with the new class line, but total_commits is still computed above. Monthly wrapped summaries no longer surface commit volume.

Suggested fix shape

  • Rename your new helper (e.g. assign_daily_rpg_class) and remove the dead duplicate at line 209, or delete the old dict-returning one and migrate its two callers (tui.py:183-195 and tests/test_insights.py:176-201). Renaming is the lower-risk path.
  • Restore the COMMITS: row in _render_wrapped_view_ui instead of replacing it. You have header lines to spare.
  • Add a test for the new helper (empty sessions, no commands, single command, top-cmd fallback to Scripting Shaman (foo)).

Smaller notes

  • level = min(100, max(1, total_cmds // 50)) means 50 commands = Level 1, 100 = Level 2. The issue's example is "Level 12 Regex Sorcerer", which is 600 commands. Either drop the threshold or normalize against the dominant-class share rather than raw totals, or the level will feel arbitrary.
  • cmd.split()[0] won't match a top command if shell history stores it as a path (e.g. ~/.local/bin/python). Strip the basename.
  • class_map doesn't include python3, pip, pytest, npx, pnpm, yarn, gh, make, awk, sed. The old dict-returning version covers more ground via lower_cmd.split() substring match. Worth keeping that coverage in the new map.
  • Trailing whitespace on a few blank lines inside the new function (PEP-8 nit, not blocking).

Happy to approve once the duplicate is resolved and CI is green.

Comment thread termstory/insights.py Outdated
break
return streak

def assign_rpg_class(sessions: List[Session]) -> str:

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.

This is the duplicate function. There's already an assign_rpg_class(sessions) -> Dict[str, Any] further down in this same file (around line 548 on the PR head). In Python the last def at module scope wins, so from termstory.insights import assign_rpg_class resolves to the dict version, not this new string version. Rename this one (e.g. assign_daily_rpg_class) and update the three call sites in termstory/tui.py to use the new name, or alternatively delete the old dict version and migrate its callers (tui.py:183-195 and tests/test_insights.py:176-201).

Comment thread termstory/tui.py Outdated
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]DAILY CLASS:[/] [dim]{rpg_class}[/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 (MarkupError: auto closing tag ('[/]') has nothing to close). Because of the duplicate assign_rpg_class at line 548, rpg_class here is actually a dict, not a string. The f-string interpolates the dict repr ({'class_name': 'Regex Sorcerer', ...}) into a Rich markup string, and the [ in the repr breaks the parser. Fix the duplicate upstream and this resolves.

Comment thread termstory/tui.py Outdated
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]DAILY CLASS:[/] [dim]{rpg_class}[/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 MarkupError root cause as the timeframe header. Once the duplicate assign_rpg_class is resolved, this line is fine.

Comment thread termstory/tui.py Outdated
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]DAILY CLASS:[/] [dim]{rpg_class}[/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 MarkupError root cause. Fix the duplicate upstream.

Comment thread termstory/tui.py Outdated
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]DAILY CLASS:[/] [dim]{rpg_class}[/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.

Wrapping the COMMITS: row instead of replacing it keeps the commit-volume metric visible in monthly wrapped summaries. total_commits is still computed above, just no longer rendered.

@bitflicker64

bitflicker64 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Quick housekeeping note: there are 4 stray "test" review stubs above from when I was probing the API to confirm which diff paths GitHub accepts for inline comments. I couldn't delete them after the fact (GitHub only allows deleting pending reviews). Sorry for the noise. The real review with inline comments is the CHANGES_REQUESTED one directly above. — bitflicker

This fixes issues where tests were failing due to un-awaited background threads (like get_github_avatar_ascii) and dynamic ID delays during view mounting.
@git-miku git-miku Bot added size/M PR changes ~109 lines kind/tests kind/tests and removed size/S PR changes ~38 lines labels Jul 15, 2026
@Diwakar-odds

Copy link
Copy Markdown
Contributor Author

I've resolved the duplicate �ssign_rpg_class function (renamed the new one to �ssign_daily_rpg_class), restored the COMMITS line in the wrapped view, added tests for the new helper, fixed the thresholds, included the extra commands in the class map, and resolved the race conditions that were causing CI to fail. CI should be fully green now! Please review.

Comment thread termstory/tui.py Outdated
@bitflicker64

Copy link
Copy Markdown
Owner

:miku /label good-backend

@git-miku git-miku Bot added the good-backend good-backend label Jul 15, 2026
@git-miku

git-miku Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

miku /label

🏷️ Added label good-backend.

@bitflicker64

Copy link
Copy Markdown
Owner

Get this merch, and you'll get the maximum points you can from me.

@bitflicker64

Copy link
Copy Markdown
Owner

Can you confirm if these labels are stackable? If yes, I can stack them too.

@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 15, 2026
@git-miku

git-miku Bot commented Jul 15, 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 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. ⭐

@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
…ug/traceback; restore commits source and legacy headers; remove broken DAILY CLASS insert in Timeframe; restore daily chronicle ai/status fields
@git-miku

git-miku Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the PR, @Diwakar-odds!

To get this PR reviewed, please ⭐ star this repo — it's free and helps others discover it. Once starred, run :miku /check-star on this PR and I'll start helping out.

A do-not-merge/needs-star label has been applied to block merge until then.

@git-miku

git-miku Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the PR, @Diwakar-odds!

To get this PR reviewed, please ⭐ star this repo — it's free and helps others discover it. Once starred, run :miku /check-star on this PR and I'll start helping out.

A do-not-merge/needs-star label has been applied to block merge until then.

@bitflicker64

Copy link
Copy Markdown
Owner

/ecsoc

@git-miku git-miku Bot added the ECSoC26 label Jul 21, 2026
@git-miku

git-miku Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

miku /ecsoc

🏷️ Added ECSOC26.

@bitflicker64

Copy link
Copy Markdown
Owner

/lgtm /approve

@git-miku git-miku Bot added the lgtm This PR has been approved by a maintainer label Jul 21, 2026

@git-miku git-miku Bot left a comment

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.

Approved via Miku.

@git-miku

git-miku Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

miku /lgtm

✅ LGTM! Label added by @bitflicker64.

Use :miku /lgtm cancel to retract.


miku /approve

✅ Approved! 🎉

@bitflicker64

Copy link
Copy Markdown
Owner

/rewrite

@git-miku

git-miku Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

miku /rewrite

✅ PR description rewritten! Check the PR body above.

Preview

Summary

Adds a daily RPG class metric that analyzes command frequency to assign developer archetypes (e.g., "Level 12 Docker Demolitionist") for TUI dashboard gamification. This replaces the previous dictionary-based assign_rpg_class with a simpler string-returning assign_daily_rpg_class function and integrates the metric into Wrapped and Daily Chronicle view headers.

Changes

  • Core (termstory/insights.py):
    • Added assign_daily_rpg_class() function that returns a formatted string (e.g., "Level 12 Docker Demolitionist") instead of a dictionary
    • Updated analyze_all() to call the new function and extract the class string directly
  • TUI (termstory/tui.py):
    • Updated imports and calls in calculate_dashboard_stats() to use assign_daily_rpg_class
    • Added "DAILY CLASS" metric to Wrapped view header in _render_wrapped_view_ui()
    • Added "DAILY CLASS" metric to Daily Chronicle view header in render_daily_chronicle_view()
    • Added traceback import in refresh_details_canvas() for debugging
  • Tests (tests/test_insights.py):
    • Added test_assign_daily_rpg_class() covering empty sessions, known commands (kubectl), and fallback for unknown commands

Architecture / Flow

flowchart LR
    A[Session Commands] --> B[assign_daily_rpg_class]
    B --> C[Command Frequency Count]
    C --> D[Class Map Lookup]
    D --> E[Level Calculation]
    E --> F[Formatted String Output]
    F --> G[TUI Headers]
    F --> H[analyze_all]
Loading

Fixes

Testing

Run pytest tests/test_insights.py::test_assign_daily_rpg_class to verify the new function handles empty sessions, known commands, and fallback scenarios. To test the TUI integration, run termstory gui and navigate to any Wrapped or Daily Chronicle view to confirm the "DAILY CLASS" metric renders correctly in the header.

Notes

The new assign_daily_rpg_class() function returns a string directly, unlike the previous assign_rpg_class() which returned a dictionary with a "class_name" key. This is an internal API change but maintains backward compatibility in the analyze_all() return value. The function includes special handling for piped commands (defaults to "grep" for Regex Sorcerer logic) and strips basename from command paths using os.path.basename().

@git-miku

git-miku Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the PR, @Diwakar-odds!

To get this PR reviewed, please ⭐ star this repo — it's free and helps others discover it. Once starred, run :miku /check-star on this PR and I'll start helping out.

A do-not-merge/needs-star label has been applied to block merge until then.

@bitflicker64 bitflicker64 removed the do-not-merge/needs-star PR author has not starred this repo — run /check-star after starring label Jul 21, 2026
@bitflicker64
bitflicker64 merged commit 727b365 into bitflicker64:main Jul 21, 2026
6 checks passed
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 ECSoC26-L2 ECSoC26 good-backend good-backend kind/tests kind/tests lgtm This PR has been approved by a maintainer size/M PR changes ~109 lines triage/triaged This PR has been triaged by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Daily RPG Class & Archetype Assigner 🧙‍♂️

2 participants