Skip to content

Commit e4c4fd1

Browse files
committed
fix(plugin): honor CODINGBUDDY_HUD_STATE_FILE for clarification budget (#1433)
- Pass CODINGBUDDY_HUD_STATE_FILE env var to read_hud_state() and update_hud_state() in standalone clarification budget persistence - Previously, custom HUD state path was ignored causing budget to not decrement across clarification rounds - Add regression tests for custom path budget decrement (3→2→1) and default path behavior preservation Closes #1433
1 parent 7a66c36 commit e4c4fd1

2 files changed

Lines changed: 100 additions & 3 deletions

File tree

packages/claude-code-plugin/hooks/test_user_prompt_submit.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,96 @@ def test_no_keyword_no_forecast(self):
614614
assert "Permissions:" not in result.stdout
615615

616616

617+
class TestClarificationBudgetHudFile:
618+
"""Regression tests for #1433: CODINGBUDDY_HUD_STATE_FILE must be honored
619+
when persisting standalone clarification question budget."""
620+
621+
def _run_hook(self, prompt, home_dir, hud_state_file):
622+
import os as _os
623+
624+
hook_path = Path(__file__).parent / "user-prompt-submit.py"
625+
input_data = json.dumps({"prompt": prompt})
626+
env = _os.environ.copy()
627+
env["HOME"] = str(home_dir)
628+
env["CLAUDE_PROJECT_DIR"] = str(home_dir)
629+
env["CODINGBUDDY_HUD_STATE_FILE"] = str(hud_state_file)
630+
env.pop("CODINGBUDDY_RULES_DIR", None)
631+
return subprocess.run(
632+
[sys.executable, str(hook_path)],
633+
input=input_data,
634+
capture_output=True,
635+
text=True,
636+
env=env,
637+
cwd=str(home_dir),
638+
)
639+
640+
def _init_hud(self, hud_file):
641+
_lib_dir = Path(__file__).parent / "lib"
642+
if str(_lib_dir) not in sys.path:
643+
sys.path.insert(0, str(_lib_dir))
644+
from hud_state import init_hud_state
645+
init_hud_state("test-session", "5.0.0", state_file=str(hud_file))
646+
647+
def _read_hud(self, hud_file):
648+
return json.loads(Path(hud_file).read_text())
649+
650+
def test_custom_hud_file_budget_decrements(self):
651+
"""Ambiguous prompt twice with custom HUD file: budget goes 3→2→1."""
652+
import tempfile
653+
654+
with tempfile.TemporaryDirectory() as tmpdir:
655+
Path(tmpdir, ".claude").mkdir()
656+
hud_file = Path(tmpdir) / "custom-hud.json"
657+
self._init_hud(hud_file)
658+
659+
# First run: budget 3 → 2
660+
r1 = self._run_hook("PLAN: improve auth flow", tmpdir, hud_file)
661+
assert r1.returncode == 0
662+
assert "CLARIFICATION REQUIRED" in r1.stdout
663+
state1 = self._read_hud(hud_file)
664+
assert state1["questionBudget"] == 2
665+
666+
# Second run: budget 2 → 1
667+
r2 = self._run_hook("PLAN: improve auth flow", tmpdir, hud_file)
668+
assert r2.returncode == 0
669+
state2 = self._read_hud(hud_file)
670+
assert state2["questionBudget"] == 1
671+
672+
def test_default_path_still_works(self):
673+
"""Without CODINGBUDDY_HUD_STATE_FILE, default path behavior unchanged."""
674+
import tempfile
675+
import os as _os
676+
677+
with tempfile.TemporaryDirectory() as tmpdir:
678+
Path(tmpdir, ".claude").mkdir()
679+
# Use default path under custom HOME
680+
codingbuddy_dir = Path(tmpdir) / ".codingbuddy"
681+
codingbuddy_dir.mkdir()
682+
hud_file = codingbuddy_dir / "hud-state.json"
683+
self._init_hud(hud_file)
684+
685+
hook_path = Path(__file__).parent / "user-prompt-submit.py"
686+
input_data = json.dumps({"prompt": "PLAN: fix something"})
687+
env = _os.environ.copy()
688+
env["HOME"] = str(tmpdir)
689+
env["CLAUDE_PROJECT_DIR"] = str(tmpdir)
690+
env.pop("CODINGBUDDY_HUD_STATE_FILE", None)
691+
env.pop("CODINGBUDDY_RULES_DIR", None)
692+
693+
result = subprocess.run(
694+
[sys.executable, str(hook_path)],
695+
input=input_data,
696+
capture_output=True,
697+
text=True,
698+
env=env,
699+
cwd=str(tmpdir),
700+
)
701+
assert result.returncode == 0
702+
assert "CLARIFICATION REQUIRED" in result.stdout
703+
state = self._read_hud(hud_file)
704+
assert state["questionBudget"] == 2
705+
706+
617707
if __name__ == "__main__":
618708
import pytest
619709
pytest.main([__file__, "-v"])

packages/claude-code-plugin/hooks/user-prompt-submit.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,15 @@ def main():
9595
# the self-contained fallback is active and no MCP server
9696
# is required for mode handling.
9797
print("# Backend: standalone (self-contained, no MCP required)")
98-
# Read persisted question budget from HUD state (#1371)
98+
# Read persisted question budget from HUD state (#1371, #1433)
9999
_question_budget = None
100+
_hud_file = os.environ.get("CODINGBUDDY_HUD_STATE_FILE")
100101
try:
101102
from hud_state import read_hud_state
102-
_hud = read_hud_state(fill_defaults=True)
103+
_hud_kw = {"fill_defaults": True}
104+
if _hud_file:
105+
_hud_kw["state_file"] = _hud_file
106+
_hud = read_hud_state(**_hud_kw)
103107
_question_budget = _hud.get("questionBudget")
104108
except Exception:
105109
pass
@@ -118,7 +122,10 @@ def main():
118122
and detected_mode in ("PLAN", "AUTO")
119123
and "CLARIFICATION REQUIRED" in instructions):
120124
new_budget = max((_question_budget - 1), 0)
121-
update_hud_state(questionBudget=new_budget)
125+
_upd_kw = {"questionBudget": new_budget}
126+
if _hud_file:
127+
_upd_kw["state_file"] = _hud_file
128+
update_hud_state(**_upd_kw)
122129
except Exception:
123130
pass
124131
# Permission forecast for standalone mode (#1418)

0 commit comments

Comments
 (0)