feat(plugins): add context_indicator with prompt circle + /context command#342
Open
mmcguff wants to merge 4 commits into
Open
feat(plugins): add context_indicator with prompt circle + /context command#342mmcguff wants to merge 4 commits into
mmcguff wants to merge 4 commits into
Conversation
Adds a small ergonomic plugin that surfaces context-window usage at a
glance:
* Inserts a colored circle into the terminal prompt right after the dog
emoji 🐶 reflecting how full the active agent's context window is:
- 🟢 under 30%
- 🟡 30%–60%
- 🔴 over 60%
* Adds a `/context` slash command that prints a token-usage breakdown
(messages vs. overhead vs. total / capacity) plus a progress bar.
Implementation notes:
* Plugin-only — no edits to core. Hooks `startup`, `custom_command`,
and `custom_command_help` per CONTRIBUTING.md.
* Idempotent prompt patch on `get_prompt_with_active_model`; original
is stashed on the module so re-installation is a no-op.
* Token math reuses the agent's existing
`estimate_tokens_for_message` + `_estimate_context_overhead` +
`_get_model_context_length` so the indicator stays in lockstep with
the numbers the compaction subsystem already computes.
* Defensive everywhere: any exception inside usage computation hides
the indicator instead of crashing the prompt.
Tests:
* `tests/plugins/test_context_indicator_plugin.py` — 22 cases covering
threshold buckets, defensive None paths, idempotent patching,
indicator placement, and the `/context` command (success + empty).
There was a problem hiding this comment.
Pull request overview
Adds a new context_indicator plugin that surfaces context-window usage both inline in the prompt (🟢/🟡/🔴 badge) and via a /context slash command that prints a token breakdown and progress bar.
Changes:
- Introduces
code_puppy.plugins.context_indicatorwith token usage computation and callback registration (prompt patch +/context). - Adds a dedicated unit test suite covering thresholds, prompt injection behavior, and slash-command output.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
code_puppy/plugins/context_indicator/__init__.py |
Adds plugin package docstring describing the feature and /context command. |
code_puppy/plugins/context_indicator/usage.py |
Implements ContextUsage, threshold→indicator mapping, and current-agent usage estimation. |
code_puppy/plugins/context_indicator/register_callbacks.py |
Registers startup prompt patch and /context command; formats the usage report/progress bar. |
tests/plugins/test_context_indicator_plugin.py |
Adds tests for indicator thresholds, defensive behavior, prompt insertion, and /context output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+20
to
+36
| def _ensure_agent_manager_stub(): | ||
| """Stub ``code_puppy.agents.agent_manager`` so ``patch()`` can target it. | ||
|
|
||
| Importing the real module pulls in MCP dependencies that aren't installed | ||
| in the test environment. The plugin only ever calls | ||
| ``get_current_agent`` — a stub with that attribute is plenty. | ||
| """ | ||
| if "code_puppy.agents.agent_manager" in sys.modules: | ||
| return | ||
| stub = MagicMock() | ||
| stub.get_current_agent = MagicMock(side_effect=RuntimeError("unstubbed")) | ||
| sys.modules["code_puppy.agents.agent_manager"] = stub | ||
| # Also ensure parent ``code_puppy.agents`` namespace knows about it. | ||
| agents_pkg = sys.modules.get("code_puppy.agents") | ||
| if agents_pkg is not None: | ||
| setattr(agents_pkg, "agent_manager", stub) | ||
|
|
Contributor
Author
There was a problem hiding this comment.
@copilot apply changes based on this feedback
Comment on lines
+77
to
+90
| try: | ||
| history = agent.get_message_history() or [] | ||
| except Exception: | ||
| history = [] | ||
|
|
||
| try: | ||
| used = sum(agent.estimate_tokens_for_message(m) for m in history) | ||
| except Exception: | ||
| used = 0 | ||
|
|
||
| try: | ||
| overhead = agent._estimate_context_overhead() | ||
| except Exception: | ||
| overhead = 0 |
Comment on lines
+114
to
+120
| f"{usage.indicator} Context usage: {usage.percent:.1f}%\n" | ||
| f" [{bar}]\n" | ||
| f" Messages : {usage.used_tokens:,} tokens\n" | ||
| f" Overhead : {usage.overhead_tokens:,} tokens (system prompt + tools)\n" | ||
| f" Total : {usage.total_tokens:,} / {usage.capacity:,} tokens\n" | ||
| f" Buckets : 🟢 <30% 🟡 30–60% 🔴 >60%" | ||
| ) |
Comment on lines
+49
to
+55
| def pick_indicator(proportion: float) -> str: | ||
| """Pick the colored-circle emoji for a given usage proportion (0..1).""" | ||
| if proportion < GREEN_THRESHOLD: | ||
| return GREEN_CIRCLE | ||
| if proportion < YELLOW_THRESHOLD: | ||
| return YELLOW_CIRCLE | ||
| return RED_CIRCLE |
…ys.modules leakage The previous helper permanently injected a MagicMock as code_puppy.agents.agent_manager into sys.modules at import time. Other tests that import the real agent_manager later in the session would pick up the stub instead, leading to order-dependent failures. Replace it with a 'stub_agent_manager' fixture using monkeypatch.setitem(sys.modules, ...), which is automatically torn down when the test ends. Tests that don't need to fake out get_current_agent no longer carry the stub at all. Per review feedback on PR mpfaffenberger#342.
Owner
|
can you make sure linters / tests pass? |
Contributor
Author
I’ll be working on this today. I got pulled into a few other priorities earlier, but I’ll let you know as soon as it’s ready. |
- Align bucket text/docs with actual threshold logic (≥60% is red, not >60%). Comment + /context legend now read '🟢 <30% 🟡 30–<60% 🔴 ≥60%' to match pick_indicator()'s sharp '<' comparisons. - get_current_usage() now returns None on ANY history/estimator/ capacity exception instead of silently falling back to zeros, which could surface a misleading 🟢 indicator. Hiding the badge is the honest behavior. - Add tests covering the new strict-None paths for estimator and overhead failures.
Contributor
Author
|
Addressed Copilot's review in 0c19ac3:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a tiny ergonomics plugin that surfaces context-window usage:
/contextslash command that prints a token-usage breakdown(messages vs. system/tools overhead vs. total / capacity) plus a
little progress bar.
Demo
Design
code_puppy/command_line/or anywherein core. Uses the documented
startup,custom_command, andcustom_command_helphooks perCONTRIBUTING.md.get_prompt_with_active_model—original is stashed on the module so re-installation is a no-op
(mirrors the pattern from
prompt_newline).(
estimate_tokens_for_message,_estimate_context_overhead,_get_model_context_length) so the indicator stays in lockstep withthe numbers the compaction subsystem already computes.
hides the indicator instead of crashing the prompt — the colored
circle is a status badge, not a load-bearing dependency.
usage.pyknows tokens,register_callbacks.pyknows prompt-toolkit and slash commands. ~100lines each, well under the 600-line cap.
Tests
tests/plugins/test_context_indicator_plugin.py— 22 cases covering:ContextUsageproportion / percent / indicator mathNonereturns when no agent / capacity is zero / imports fail/contextcommand (success path, no-usage path, ignores unrelated names)Files
No new dependencies. No core file modifications.