diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 945d9914..eb56e9b1 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -40,7 +40,7 @@ "prettier": "^3.4.2", "tailwindcss": "^4", "typescript": "^5", - "vite": "^7.3.1", + "vite": "^8.0.5", "vitest": "^4.0.18" } } diff --git a/apps/landing-page/package.json b/apps/landing-page/package.json index 24c6071a..89265a00 100644 --- a/apps/landing-page/package.json +++ b/apps/landing-page/package.json @@ -64,7 +64,7 @@ "tailwindcss": "^4", "tw-animate-css": "^1.4.0", "typescript": "^5", - "vite": "^7.3.1", + "vite": "^8.0.5", "vitest": "^4.0.18" } } diff --git a/packages/claude-code-plugin/hooks/lib/hud_helpers.py b/packages/claude-code-plugin/hooks/lib/hud_helpers.py index 795bd9b7..f388d538 100644 --- a/packages/claude-code-plugin/hooks/lib/hud_helpers.py +++ b/packages/claude-code-plugin/hooks/lib/hud_helpers.py @@ -16,10 +16,35 @@ import json import os +import re from pathlib import Path from typing import Dict, List, Optional -from hud_state import update_hud_state +from hud_state import read_hud_state, update_hud_state + +# Council lifecycle stages — forward-only progression (#1368) +COUNCIL_STAGES = ("opening", "reviewing", "consensus", "done") + +# Tool completions that signal specialist analysis is underway +_SPECIALIST_SIGNAL_TOOLS = frozenset({ + "Agent", + "mcp__codingbuddy__analyze_task", + "mcp__codingbuddy__prepare_parallel_agents", + "mcp__codingbuddy__dispatch_agents", + "mcp__codingbuddy__generate_checklist", +}) + +# Tool completions that signal the council is converging on decisions +_CONSENSUS_SIGNAL_TOOLS = frozenset({ + "Edit", + "Write", + "mcp__codingbuddy__update_context", +}) + +# Quality-check command patterns for blocker detection +_TEST_CMD_PATTERNS = ("pytest", "vitest", "jest", "yarn test", "npm test") +_TYPECHECK_CMD_PATTERNS = ("tsc", "type-check", "typecheck") +_LINT_CMD_PATTERNS = ("eslint", "prettier --check", "yarn lint", "npm run lint") _DEFAULT_PLUGINS_FILE = str( Path.home() / ".claude" / "plugins" / "installed_plugins.json" @@ -164,8 +189,9 @@ def on_tool_end( ) -> None: """Record stable post-action state after a tool completes. - Called from PostToolUse. Captures agent handoffs and phase - transitions that are evident from tool outputs. + Called from PostToolUse. Captures agent handoffs, phase + transitions, council stage advancement, and blocker counts + evident from tool outputs (#1368). Args: tool_name: Name of the completed tool. @@ -190,6 +216,25 @@ def on_tool_end( updates["currentMode"] = mode updates["phase"] = phase + # Council stage advancement (#1368) — only read state when the + # tool could actually trigger a transition, avoiding disk I/O + # on every Read/Grep/Glob call. + _council_tools = _SPECIALIST_SIGNAL_TOOLS | _CONSENSUS_SIGNAL_TOOLS | {"mcp__codingbuddy__parse_mode"} + if tool_name in _council_tools: + sf_kwargs = {"state_file": state_file} if state_file else {} + state = read_hud_state(fill_defaults=True, **sf_kwargs) + + if state.get("councilActive"): + current_stage = state.get("councilStage", "") + next_stage = _infer_council_advance(tool_name, current_stage) + if next_stage: + updates["councilStage"] = next_stage + + # Blocker detection (#1368) + blocker_count = _detect_blocker_count(tool_name, tool_input, tool_output) + if blocker_count is not None: + updates["blockerCount"] = blocker_count + if updates: if state_file: update_hud_state(state_file=state_file, **updates) @@ -307,6 +352,80 @@ def on_council_update( # ---- private helpers ---- +def _infer_council_advance( + tool_name: str, + current_stage: str, +) -> Optional[str]: + """Infer the next council stage from a completed tool. + + Stage transitions are forward-only: + opening → reviewing → consensus → done + + Returns the new stage name, or None if no transition applies. + """ + if not current_stage or current_stage not in COUNCIL_STAGES: + return None + + if current_stage == "done": + return None + + if current_stage == "opening" and tool_name in _SPECIALIST_SIGNAL_TOOLS: + return "reviewing" + + if current_stage == "reviewing" and tool_name in _CONSENSUS_SIGNAL_TOOLS: + return "consensus" + + if current_stage == "consensus" and tool_name == "mcp__codingbuddy__parse_mode": + return "done" + + return None + + +def _detect_blocker_count( + tool_name: str, + tool_input: dict, + tool_output: str, +) -> Optional[int]: + """Detect blocker count from quality-check Bash output. + + Returns: + int >= 0 when the tool is a quality-check command + (0 means all checks passed — clear blockers). + None for non-quality tools (meaning "don't touch blockerCount"). + """ + if tool_name != "Bash": + return None + + cmd = tool_input.get("command", "") + output = (tool_output or "").lower() + + # Test runner + if any(p in cmd for p in _TEST_CMD_PATTERNS): + match = re.search(r"(\d+)\s+failed", output) + if match: + return int(match.group(1)) + if "failed" in output or "fail" in output: + return 1 + return 0 + + # Type checker + if any(p in cmd for p in _TYPECHECK_CMD_PATTERNS): + errors = re.findall(r"error ts\d+", output) + if errors: + return len(errors) + if "error" in output: + return 1 + return 0 + + # Linter + if any(p in cmd for p in _LINT_CMD_PATTERNS): + if "error" in output: + return 1 + return 0 + + return None + + def _detect_focus(tool_name: str, tool_input: dict) -> Optional[str]: """Infer a human-readable focus label from the current tool call.""" if tool_name == "Edit" or tool_name == "Write": diff --git a/packages/claude-code-plugin/hooks/tests/test_hud_helpers.py b/packages/claude-code-plugin/hooks/tests/test_hud_helpers.py index 4d40fd35..8df9c5fa 100644 --- a/packages/claude-code-plugin/hooks/tests/test_hud_helpers.py +++ b/packages/claude-code-plugin/hooks/tests/test_hud_helpers.py @@ -29,6 +29,8 @@ _detect_focus, _detect_strategy, _extract_mode_from_parse_mode, + _infer_council_advance, + _detect_blocker_count, ) @@ -630,3 +632,338 @@ def test_returns_none_on_malformed_json(self, tmp_path): plugins_json.write_text("not valid json{{{") result = read_installed_version(plugins_file=str(plugins_json)) assert result is None + + +# ---- _infer_council_advance (#1368) ---- + +class TestInferCouncilAdvance: + """Council stage advancement heuristic (#1368).""" + + # opening → reviewing + def test_opening_to_reviewing_on_agent_tool(self): + assert _infer_council_advance("Agent", "opening") == "reviewing" + + def test_opening_to_reviewing_on_analyze_task(self): + assert _infer_council_advance("mcp__codingbuddy__analyze_task", "opening") == "reviewing" + + def test_opening_to_reviewing_on_dispatch_agents(self): + assert _infer_council_advance("mcp__codingbuddy__dispatch_agents", "opening") == "reviewing" + + def test_opening_to_reviewing_on_generate_checklist(self): + assert _infer_council_advance("mcp__codingbuddy__generate_checklist", "opening") == "reviewing" + + def test_opening_to_reviewing_on_prepare_parallel_agents(self): + assert _infer_council_advance("mcp__codingbuddy__prepare_parallel_agents", "opening") == "reviewing" + + # reviewing → consensus + def test_reviewing_to_consensus_on_edit(self): + assert _infer_council_advance("Edit", "reviewing") == "consensus" + + def test_reviewing_to_consensus_on_write(self): + assert _infer_council_advance("Write", "reviewing") == "consensus" + + def test_reviewing_to_consensus_on_update_context(self): + assert _infer_council_advance("mcp__codingbuddy__update_context", "reviewing") == "consensus" + + # consensus → done + def test_consensus_to_done_on_parse_mode(self): + assert _infer_council_advance("mcp__codingbuddy__parse_mode", "consensus") == "done" + + # no-ops + def test_no_advance_on_unrelated_tool(self): + assert _infer_council_advance("Read", "opening") is None + assert _infer_council_advance("Grep", "reviewing") is None + assert _infer_council_advance("Bash", "consensus") is None + + def test_no_backward_transition(self): + assert _infer_council_advance("Agent", "consensus") is None + assert _infer_council_advance("Edit", "opening") is None + + def test_no_advance_from_done(self): + assert _infer_council_advance("Agent", "done") is None + assert _infer_council_advance("Edit", "done") is None + + def test_no_advance_when_stage_empty(self): + assert _infer_council_advance("Agent", "") is None + + def test_no_advance_when_stage_unknown(self): + assert _infer_council_advance("Agent", "unknown-stage") is None + + +# ---- _detect_blocker_count (#1368) ---- + +class TestDetectBlockerCount: + """Blocker detection from quality-check tool output (#1368).""" + + # Test runners + def test_pytest_failure_count(self): + output = "FAILED tests/test_a.py - 3 failed, 7 passed" + assert _detect_blocker_count("Bash", {"command": "pytest tests/"}, output) == 3 + + def test_pytest_single_failure(self): + output = "FAILED tests/test_a.py\n1 failed" + assert _detect_blocker_count("Bash", {"command": "python3 -m pytest"}, output) == 1 + + def test_pytest_pass_clears_blockers(self): + output = "10 passed in 2.3s" + assert _detect_blocker_count("Bash", {"command": "pytest tests/ -v"}, output) == 0 + + def test_vitest_failure(self): + output = "Tests Failed: 2 failed | 8 passed" + assert _detect_blocker_count("Bash", {"command": "npx vitest run"}, output) == 2 + + def test_jest_pass(self): + output = "Tests: 5 passed, 5 total" + assert _detect_blocker_count("Bash", {"command": "npx jest"}, output) == 0 + + def test_yarn_test_failure_fallback(self): + output = "Test failed." + assert _detect_blocker_count("Bash", {"command": "yarn test"}, output) == 1 + + # Type checkers + def test_tsc_error_count(self): + output = "src/a.ts(1,1): error TS2304: ...\nsrc/b.ts(2,2): error TS1005: ..." + assert _detect_blocker_count("Bash", {"command": "npx tsc --noEmit"}, output) == 2 + + def test_tsc_pass(self): + assert _detect_blocker_count("Bash", {"command": "npx tsc --noEmit"}, "") == 0 + + def test_typecheck_generic_error(self): + output = "error: something went wrong" + assert _detect_blocker_count("Bash", {"command": "yarn type-check"}, output) == 1 + + # Linters + def test_eslint_error(self): + output = "1 error and 2 warnings" + assert _detect_blocker_count("Bash", {"command": "npx eslint src/"}, output) == 1 + + def test_eslint_pass(self): + assert _detect_blocker_count("Bash", {"command": "npx eslint src/"}, "") == 0 + + def test_prettier_check_error(self): + output = "error: some files are not formatted" + assert _detect_blocker_count("Bash", {"command": "prettier --check ."}, output) == 1 + + def test_yarn_lint_error(self): + output = "1 error found" + assert _detect_blocker_count("Bash", {"command": "yarn lint"}, output) == 1 + + def test_npm_run_lint_pass(self): + assert _detect_blocker_count("Bash", {"command": "npm run lint"}, "") == 0 + + # Non-quality tools + def test_returns_none_for_non_bash_tool(self): + assert _detect_blocker_count("Edit", {}, "anything") is None + assert _detect_blocker_count("Agent", {}, "anything") is None + + def test_returns_none_for_non_quality_bash(self): + assert _detect_blocker_count("Bash", {"command": "git status"}, "output") is None + assert _detect_blocker_count("Bash", {"command": "ls -la"}, "") is None + + # Edge cases + def test_empty_output_for_test_runner(self): + assert _detect_blocker_count("Bash", {"command": "pytest"}, "") == 0 + + def test_none_output_for_test_runner(self): + assert _detect_blocker_count("Bash", {"command": "pytest"}, None) == 0 + + def test_npm_test_failure(self): + output = "5 failed, 10 passed" + assert _detect_blocker_count("Bash", {"command": "npm test"}, output) == 5 + + +# ---- on_tool_end council integration (#1368) ---- + +class TestOnToolEndCouncilAdvancement: + """PostToolUse: on_tool_end council stage + blocker integration (#1368).""" + + def _seed_council(self, state_file, stage="opening"): + from hud_state import update_hud_state + update_hud_state( + state_file=state_file, + councilActive=True, + councilStage=stage, + councilCast=["technical-planner", "security-specialist"], + ) + + def test_advances_opening_to_reviewing_on_agent(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + self._seed_council(state_file, "opening") + on_tool_end("Agent", {"prompt": "review security"}, "{}", state_file=state_file) + state = _read(state_file) + assert state["councilStage"] == "reviewing" + + def test_advances_reviewing_to_consensus_on_edit(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + self._seed_council(state_file, "reviewing") + on_tool_end("Edit", {"file_path": "/src/a.py"}, "", state_file=state_file) + state = _read(state_file) + assert state["councilStage"] == "consensus" + + def test_advances_consensus_to_done_on_parse_mode(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + self._seed_council(state_file, "consensus") + on_tool_end( + "mcp__codingbuddy__parse_mode", + {"prompt": "EVAL: review"}, + "{}", + state_file=state_file, + ) + state = _read(state_file) + assert state["councilStage"] == "done" + + def test_no_advance_when_council_inactive(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + on_tool_end("Agent", {}, "{}", state_file=state_file) + state = _read(state_file) + assert state["councilStage"] == "" + + def test_updates_blocker_count_on_test_failure(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + on_tool_end( + "Bash", + {"command": "pytest tests/"}, + "FAILED - 3 failed, 7 passed", + state_file=state_file, + ) + state = _read(state_file) + assert state["blockerCount"] == 3 + + def test_clears_blockers_on_test_pass(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + from hud_state import update_hud_state + update_hud_state(state_file=state_file, blockerCount=5) + + on_tool_end( + "Bash", + {"command": "pytest tests/ -v"}, + "10 passed in 1.2s", + state_file=state_file, + ) + state = _read(state_file) + assert state["blockerCount"] == 0 + + def test_stage_and_blocker_independent(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + self._seed_council(state_file, "opening") + + on_tool_end("Agent", {}, "{}", state_file=state_file) + state = _read(state_file) + assert state["councilStage"] == "reviewing" + assert state["blockerCount"] == 0 + + on_tool_end( + "Bash", + {"command": "pytest tests/"}, + "2 failed", + state_file=state_file, + ) + state = _read(state_file) + assert state["councilStage"] == "reviewing" + assert state["blockerCount"] == 2 + + def test_no_blocker_update_for_non_quality_bash(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + from hud_state import update_hud_state + update_hud_state(state_file=state_file, blockerCount=1) + + on_tool_end("Bash", {"command": "git status"}, "clean", state_file=state_file) + state = _read(state_file) + assert state["blockerCount"] == 1 + + def test_preserves_council_active_on_advance(self, state_file, monkeypatch): + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + self._seed_council(state_file, "opening") + on_tool_end("Agent", {}, "{}", state_file=state_file) + state = _read(state_file) + assert state["councilActive"] is True + assert state["councilCast"] == ["technical-planner", "security-specialist"] + + +# ---- Full lifecycle with council (#1368) ---- + +class TestFullLifecycleWithCouncil: + """End-to-end test of council state through a complete request lifecycle (#1368).""" + + def test_council_lifecycle_through_request(self, tmp_path, monkeypatch): + sf = str(tmp_path / "hud-state.json") + + # 1. SessionStart: init + init_hud_state("council-lifecycle", "5.3.0", state_file=sf) + state = _read(sf) + assert state["councilActive"] is False + + # 2. UserPromptSubmit: mode entry seeds council + on_mode_entry( + "PLAN", + council_preset={ + "primary": "technical-planner", + "specialists": ["security-specialist", "architecture-specialist"], + }, + state_file=sf, + ) + state = _read(sf) + assert state["councilActive"] is True + assert state["councilStage"] == "opening" + assert len(state["councilCast"]) == 3 + + # 3. PreToolUse: agent dispatched + monkeypatch.setenv("CODINGBUDDY_ACTIVE_AGENT", "technical-planner") + on_tool_start("Agent", {"prompt": "analyze architecture"}, state_file=sf) + + # 4. PostToolUse: Agent complete → opening → reviewing + on_tool_end("Agent", {"prompt": "analyze"}, '{"result": "ok"}', state_file=sf) + state = _read(sf) + assert state["councilStage"] == "reviewing" + assert state["lastHandoff"] == "technical-planner" + + # 5. PostToolUse: test failure → blocker count + monkeypatch.delenv("CODINGBUDDY_ACTIVE_AGENT", raising=False) + on_tool_end( + "Bash", + {"command": "pytest tests/ -v"}, + "FAILED tests/test_a.py - 2 failed, 8 passed", + state_file=sf, + ) + state = _read(sf) + assert state["blockerCount"] == 2 + assert state["councilStage"] == "reviewing" + + # 6. PostToolUse: Edit → reviewing → consensus + on_tool_end("Edit", {"file_path": "/src/fix.py"}, "", state_file=sf) + state = _read(sf) + assert state["councilStage"] == "consensus" + + # 7. PostToolUse: tests pass → blockers cleared + on_tool_end( + "Bash", + {"command": "pytest tests/ -v"}, + "10 passed in 1.5s", + state_file=sf, + ) + state = _read(sf) + assert state["blockerCount"] == 0 + + # 8. PostToolUse: parse_mode → consensus → done + on_tool_end( + "mcp__codingbuddy__parse_mode", + {"prompt": "EVAL: review the changes"}, + "{}", + state_file=sf, + ) + state = _read(sf) + assert state["councilStage"] == "done" + assert state["currentMode"] == "EVAL" + assert state["phase"] == "evaluating" + + # 9. New mode entry resets council + on_mode_entry("EVAL", state_file=sf) + state = _read(sf) + assert state["councilActive"] is False + assert state["councilStage"] == "" + + # 10. Stop clears everything + on_session_stop(state_file=sf) + state = _read(sf) + assert state["phase"] == "completed" + assert state["councilActive"] is False diff --git a/yarn.lock b/yarn.lock index 8b83bb3f..cf24ec1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1461,6 +1461,18 @@ __metadata: languageName: node linkType: hard +"@napi-rs/wasm-runtime@npm:^1.1.1": + version: 1.1.2 + resolution: "@napi-rs/wasm-runtime@npm:1.1.2" + dependencies: + "@tybys/wasm-util": "npm:^0.10.1" + peerDependencies: + "@emnapi/core": ^1.7.1 + "@emnapi/runtime": ^1.7.1 + checksum: 10c0/725c30ec9c480a8d0c1a6a4ce31dc6c830365d485e23ad560e143d1cb9db89a0c95fbb5b9d53c07121729817a3683db6f1ab65d7e4f38fa7482a11b15ef6c6fd + languageName: node + linkType: hard + "@nestjs/common@npm:11.1.9": version: 11.1.9 resolution: "@nestjs/common@npm:11.1.9" @@ -1681,6 +1693,13 @@ __metadata: languageName: node linkType: hard +"@oxc-project/types@npm:=0.122.0": + version: 0.122.0 + resolution: "@oxc-project/types@npm:0.122.0" + checksum: 10c0/2c64dd0db949426fd0c86d4f61eded5902e7b7b166356a825bd3a248aeaa29a495f78918f66ab78e99644b67bd7556096e2a8123cec74ca4141c604f424f4f74 + languageName: node + linkType: hard + "@parcel/watcher-android-arm64@npm:2.5.6": version: 2.5.6 resolution: "@parcel/watcher-android-arm64@npm:2.5.6" @@ -2588,6 +2607,120 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-android-arm64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-android-arm64@npm:1.0.0-rc.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-darwin-arm64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-darwin-arm64@npm:1.0.0-rc.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-darwin-x64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-darwin-x64@npm:1.0.0-rc.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rolldown/binding-freebsd-x64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-freebsd-x64@npm:1.0.0-rc.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.12" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.12" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.12" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.1.1" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rolldown/pluginutils@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/pluginutils@npm:1.0.0-rc.12" + checksum: 10c0/f785d1180ea4876bf6a6a67135822808d1c07f902409524ff1088779f7d5318f6e603d281fb107a5145c1ca54b7cabebd359629ec474ebbc2812f2cf53db4023 + languageName: node + linkType: hard + "@rolldown/pluginutils@npm:1.0.0-rc.2": version: 1.0.0-rc.2 resolution: "@rolldown/pluginutils@npm:1.0.0-rc.2" @@ -5553,7 +5686,7 @@ __metadata: recharts: "npm:^2.15.3" tailwindcss: "npm:^4" typescript: "npm:^5" - vite: "npm:^7.3.1" + vite: "npm:^8.0.5" vitest: "npm:^4.0.18" languageName: unknown linkType: soft @@ -8826,7 +8959,7 @@ __metadata: tailwindcss: "npm:^4" tw-animate-css: "npm:^1.4.0" typescript: "npm:^5" - vite: "npm:^7.3.1" + vite: "npm:^8.0.5" vitest: "npm:^4.0.18" languageName: unknown linkType: soft @@ -8864,6 +8997,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "lightningcss-darwin-arm64@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-darwin-arm64@npm:1.30.2" @@ -8871,6 +9011,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "lightningcss-darwin-x64@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-darwin-x64@npm:1.30.2" @@ -8878,6 +9025,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "lightningcss-freebsd-x64@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-freebsd-x64@npm:1.30.2" @@ -8885,6 +9039,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "lightningcss-linux-arm-gnueabihf@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-linux-arm-gnueabihf@npm:1.30.2" @@ -8892,6 +9053,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "lightningcss-linux-arm64-gnu@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-linux-arm64-gnu@npm:1.30.2" @@ -8899,6 +9067,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "lightningcss-linux-arm64-musl@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-linux-arm64-musl@npm:1.30.2" @@ -8906,6 +9081,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "lightningcss-linux-x64-gnu@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-linux-x64-gnu@npm:1.30.2" @@ -8913,6 +9095,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "lightningcss-linux-x64-musl@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-linux-x64-musl@npm:1.30.2" @@ -8920,6 +9109,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "lightningcss-win32-arm64-msvc@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-win32-arm64-msvc@npm:1.30.2" @@ -8927,6 +9123,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "lightningcss-win32-x64-msvc@npm:1.30.2": version: 1.30.2 resolution: "lightningcss-win32-x64-msvc@npm:1.30.2" @@ -8934,6 +9137,13 @@ __metadata: languageName: node linkType: hard +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "lightningcss@npm:1.30.2": version: 1.30.2 resolution: "lightningcss@npm:1.30.2" @@ -8977,6 +9187,49 @@ __metadata: languageName: node linkType: hard +"lightningcss@npm:^1.32.0": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10c0/70945bd55097af46fc9fab7f5ed09cd5869d85940a2acab7ee06d0117004a1d68155708a2d462531cea2fc3c67aefc9333a7068c80b0b78dd404c16838809e03 + languageName: node + linkType: hard + "load-esm@npm:1.0.3": version: 1.0.3 resolution: "load-esm@npm:1.0.3" @@ -10034,6 +10287,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.4": + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 10c0/e2c6023372cc7b5764719a5ffb9da0f8e781212fa7ca4bd0562db929df8e117460f00dff3cb7509dacfc06b86de924b247f504d0ce1806a37fac4633081466b0 + languageName: node + linkType: hard + "pkce-challenge@npm:^5.0.0": version: 5.0.1 resolution: "pkce-challenge@npm:5.0.1" @@ -10097,6 +10357,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.8": + version: 8.5.8 + resolution: "postcss@npm:8.5.8" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/dd918f7127ee7c60a0295bae2e72b3787892296e1d1c3c564d7a2a00c68d8df83cadc3178491259daa19ccc54804fb71ed8c937c6787e08d8bd4bedf8d17044c + languageName: node + linkType: hard + "prebuild-install@npm:^7.1.1": version: 7.1.3 resolution: "prebuild-install@npm:7.1.3" @@ -10701,6 +10972,64 @@ __metadata: languageName: node linkType: hard +"rolldown@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "rolldown@npm:1.0.0-rc.12" + dependencies: + "@oxc-project/types": "npm:=0.122.0" + "@rolldown/binding-android-arm64": "npm:1.0.0-rc.12" + "@rolldown/binding-darwin-arm64": "npm:1.0.0-rc.12" + "@rolldown/binding-darwin-x64": "npm:1.0.0-rc.12" + "@rolldown/binding-freebsd-x64": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-arm64-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-arm64-musl": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-ppc64-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-s390x-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-x64-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-x64-musl": "npm:1.0.0-rc.12" + "@rolldown/binding-openharmony-arm64": "npm:1.0.0-rc.12" + "@rolldown/binding-wasm32-wasi": "npm:1.0.0-rc.12" + "@rolldown/binding-win32-arm64-msvc": "npm:1.0.0-rc.12" + "@rolldown/binding-win32-x64-msvc": "npm:1.0.0-rc.12" + "@rolldown/pluginutils": "npm:1.0.0-rc.12" + dependenciesMeta: + "@rolldown/binding-android-arm64": + optional: true + "@rolldown/binding-darwin-arm64": + optional: true + "@rolldown/binding-darwin-x64": + optional: true + "@rolldown/binding-freebsd-x64": + optional: true + "@rolldown/binding-linux-arm-gnueabihf": + optional: true + "@rolldown/binding-linux-arm64-gnu": + optional: true + "@rolldown/binding-linux-arm64-musl": + optional: true + "@rolldown/binding-linux-ppc64-gnu": + optional: true + "@rolldown/binding-linux-s390x-gnu": + optional: true + "@rolldown/binding-linux-x64-gnu": + optional: true + "@rolldown/binding-linux-x64-musl": + optional: true + "@rolldown/binding-openharmony-arm64": + optional: true + "@rolldown/binding-wasm32-wasi": + optional: true + "@rolldown/binding-win32-arm64-msvc": + optional: true + "@rolldown/binding-win32-x64-msvc": + optional: true + bin: + rolldown: bin/cli.mjs + checksum: 10c0/0c4e5e3cdcdddce282cb2d84e1c98d6ad8d4e452d5c1402e498b35ec1060026e552dd783efc9f4ba876d7c0863b5973edc79b6a546f565e9832dc1077ec18c2c + languageName: node + linkType: hard + "rollup@npm:^4.43.0": version: 4.54.0 resolution: "rollup@npm:4.54.0" @@ -12276,8 +12605,8 @@ __metadata: linkType: hard "vite@npm:^6.0.0 || ^7.0.0": - version: 7.3.0 - resolution: "vite@npm:7.3.0" + version: 7.3.2 + resolution: "vite@npm:7.3.2" dependencies: esbuild: "npm:^0.27.0" fdir: "npm:^6.5.0" @@ -12326,26 +12655,26 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/0457c196cdd5761ec351c0f353945430fbad330e615b9eeab729c8ae163334f18acdc1d9cd7d9d673dbf111f07f6e4f0b25d4ac32360e65b4a6df9991046f3ff + checksum: 10c0/74be36907e208916f18bfec81c8eba18b869f0a170f1ece0a4dcb14874d0f0e7c022fb6c2ad896e3ee6c973fe88f53ac23b4078879ada340d8b263260868b8d4 languageName: node linkType: hard -"vite@npm:^7.3.1": - version: 7.3.1 - resolution: "vite@npm:7.3.1" +"vite@npm:^8.0.5": + version: 8.0.5 + resolution: "vite@npm:8.0.5" dependencies: - esbuild: "npm:^0.27.0" - fdir: "npm:^6.5.0" fsevents: "npm:~2.3.3" - picomatch: "npm:^4.0.3" - postcss: "npm:^8.5.6" - rollup: "npm:^4.43.0" + lightningcss: "npm:^1.32.0" + picomatch: "npm:^4.0.4" + postcss: "npm:^8.5.8" + rolldown: "npm:1.0.0-rc.12" tinyglobby: "npm:^0.2.15" peerDependencies: "@types/node": ^20.19.0 || >=22.12.0 + "@vitejs/devtools": ^0.1.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: ">=1.21.0" less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: ">=0.54.8" @@ -12359,12 +12688,14 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true + "@vitejs/devtools": + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -12381,7 +12712,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/5c7548f5f43a23533e53324304db4ad85f1896b1bfd3ee32ae9b866bac2933782c77b350eb2b52a02c625c8ad1ddd4c000df077419410650c982cd97fde8d014 + checksum: 10c0/bfc22896b2661753c01c398a058f1859bdbd3ebe55f3d8505ab629b39e5f68790c0a6f55f8644b6692b0b9b8e210f698082ef9f4fd0d76509f4a46762fbfbba2 languageName: node linkType: hard