Merge branch 'v1.1-develop' into usage-data

Conflicts:
	CHANGELOG.md
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-04-10 22:32:46 +02:00
commit c1da839cec
15 files changed
+164 -90

No files matched your search

+7 -3
View File
@@ -3,11 +3,15 @@
Status of the `main` branch. Changes prior to the next official version change will appear here.
* General:
- Add `serena init` command
- Added `serena init` and `serena setup` commands
- **Major**: added commands for hooks and documentation on recommended setup. Consider setting up the [recommended hooks](https://oraios.github.io/serena/02-usage/030_clients.html) !
- Reworked installation instructions, switching to releases on pypi for distribution. Please update your mcp startup commands!
- Add minimal usage data collection on startup (only Serena version, language backend, OS, dashboard enabled status; no personally identifiable information)
* Language Servers:
- Added Crystal language support (uses [Crystalline](https://github.com/elbywan/crystalline) language server)
- Added Crystal language support (uses [Crystalline](https://github.com/elbywan/crystalline) language server)
* JetBrains:
- `JetBrainsFindReferencingSymbolTool`: Include context lines (when using plugin version 2023.2.15+)
* Dashboard:
@@ -17,7 +21,7 @@ Status of the `main` branch. Changes prior to the next official version change w
* Fixes:
- Fix reactivation of the same project restarting language servers #1280
- Fix git commit id in version
- Fix git commit id in version string
- Fix manual server shutdown triggered by GUI tool/dashboard not cleaning everything up.
# 1.0.0
+3
View File
@@ -17,6 +17,9 @@ approaches that rely on low-level concepts like line numbers or primitive search
Practically, this means that your agent operates **faster, more efficiently and more reliably**, especially in larger and
more complex codebases.
> [!IMPORTANT]
> Don't install Serena via an MCP or plugin marketplace! They contain outdated and suboptimal installation commands. Instead, follow our [Quick Start](#quick-start) instructions.
## How Serena Works
Serena provides the necessary [tools](https://oraios.github.io/serena/01-about/035_tools.html) for coding workflows,
+6 -2
View File
@@ -180,14 +180,18 @@ Explore the full set of commands and options using the CLI itself!
Depending on your requirements, you may want to run Serena in different ways.
When applying one of these approaches, replace `serena` in commands mentioned throughout the documentation
with the respective command and options.
with the respective command and options. The same applies to `serena-hooks` commands.
### Using uvx to Run the Latest Source Version
`uvx` is part of `uv`. It can be used to run the latest version of Serena directly from the repository, without an explicit local installation.
uvx -p 3.13 --from git+https://github.com/oraios/serena serena
This was previously the main way of running Serena.
Since this has the downside that every new commit in the repository will trigger a (potentially slow) re-synchronization, an [installation](010_installation) of Serena should usually be preferred.
If you should experience timeouts when connecting the MCP server, consider switching.
If, however, the synchronisation is fast enough for you, this is still a good option.
### Running from Cloned Source
+13 -14
View File
@@ -134,14 +134,13 @@ set ENABLE_TOOL_SEARCH=true && claude
in Windows CMD to launch Claude Code.
**Hooks.**
Claude Code's agent can sometimes drift away from using MCP tools during long sessions, falling back
to built-in tools or forgetting to load Serena's instructions. Serena provides hook commands that
automatically remind the agent to use Serena's tools when appropriate.
Due to recent changes (especially dynamic tool loading) in Claude Code, the agent will often fail to make proper use
of Serena's tools, either by failing to load them in the beginning or by forgetting the instructions in a long session
(a behavior known as agent drift). To counteract this, we provide reminder hooks. We **strongly recommend** setting
up the hooks as below (or a variation thereof) for optimal performance of Serena in Claude Code.
:::{note}
Hooks are an **alpha feature** that we provide to overcome context drift in several agent harnesses
(where agents, especially those powered by Anthropic models, may forget to load and use MCP tools or
fail to read MCP instructions).
While recommended, hooks are an **alpha feature**. Provide feedback via the [GitHub issue tracker](https://github.com/serenadeai/serenade/issues) if you encounter any issues.
:::
To set up hooks, add the following to your Claude Code settings file
@@ -235,15 +234,11 @@ In both cases, proceed to enter Serena as the name, then select either global or
You should be able to see Serena in the tools overview in the AI Chat window.
**Hooks.**
VSCode's Copilot agent can sometimes over-rely on basic tools like `read_file` and `grep_search` instead of
using Serena's more efficient symbolic tools. You can use Serena's hook commands to automatically correct
for this — the hooks will simply remind the agent to use Serena's tools when appropriate.
Due to recent changes (especially dynamic tool loading) in VSCode, the agent will often fail to make proper use
of Serena's tools, either by failing to load them in the beginning or by forgetting the instructions in a long session
(a behaviour known as agent drift). To counteract this, we provide reminder hooks. We **strongly recommend** setting
up the hooks as below (or a variation thereof) for optimal performance of Serena in VSCode.
:::{note}
Hooks are an **alpha feature** that we provide to overcome context drift in several agent harnesses
(where agents, especially those powered by Anthropic models, may forget to load and use MCP tools or
fail to read MCP instructions).
:::
The hooks will:
@@ -301,6 +296,10 @@ The Codex app does not start a session in the project's directory, so when using
asking Codex to "Activate the current dir as project using serena" at the start of each session (though Codex might
do this automatically).
**Hooks.** The Codex hook system is less refined than of VSCode or Claude Code, but Codex also displays less drift
and is better in using Serena's tools out of the box. If you use the Codex app, you can consider setting up the
SessionStart hook (see above) to remind the agent to activate the project directory.
## Claude Desktop
On Windows and macOS, there are official [Claude Desktop applications by Anthropic](https://claude.ai/download); for Linux, there is an [open-source
+22
View File
@@ -0,0 +1,22 @@
<div class="news-item">
<h3>Serena v1.1: Hooks and New Release Mechanism</h3>
<p class="date">April 9, 2026</p>
<p>
<b>Installation.</b> Serena has matured and we are switching to a PyPI-based release and installation mechanism.
The recommended uv-tool-based installation decreases startup times and facilitates CLI usage.
See <a href="https://oraios.github.io/serena/02-usage/010_installation.html">updated installation instructions</a>
for more details.
</p>
<p>
<b>Improving Tool Usage via Hooks.</b> To address issues with agents not using Serena's tools (agent drift), we now provide the `serena-hooks` commands and a recommended
setup for Claude Code and VSCode.
Please read the <a href="https://oraios.github.io/serena/02-usage/030_clients.html">updated documentation</a> for more details.
We <i>strongly recommend using the hooks</i>, since recent updates in Claude Code and VSCode resulted in
reduced performance, which our hook commands compensate for.
</p>
<p>
<b>Community.</b> We opened a <a href="https://discord.com/invite/cVUNQmnV4r">Discord server</a> for our community.
</p>
</div>
-7
View File
@@ -1,7 +0,0 @@
{
"20260111": "<div class=\"news-item\">\n <h3>Extended Symbol Information, Type Hierarchy and Compact Overviews</h3>\n <p class=\"date\">January 11, 2026</p>\n <p>\n Recent commits and a new plugin release provide major new features!\n </p>\n <ul>\n <li><strong>Extended symbol information:</strong> The `find_symbol` and `find_referencing_symbols` tools (and their JetBrains variants)\n now can return more information about the symbol (specifically, docstrings and signatures).\n </li>\n <li><strong>Type hierarchy tool:</strong> A new tool &ndash; exclusive to <a href=\"https://oraios.github.io/serena/02-usage/025_jetbrains_plugin.html\">JetBrains mode</a> &ndash;\n that can fetch a symbol's hierarchy, i.e. superclasses and subclasses, which is very useful in many situations.\n </li>\n <li><strong>Compact overviews:</strong> The `get_symbols_overview` tools now return a much more compact\n representation, saving many tokens. The JetBrains variant of the tool can now return a file's docstring (LSP\n varian't can't do that yet).\n </li>\n </ul>\n <p>\n For more detailed information, see our <a href=\"https://github.com/oraios/serena/blob/main/CHANGELOG.md\">changelog</a>.\n </p>\n</div>",
"20260303": "<div class=\"news-item\">\n <h3>Nested and Global Memories</h3>\n <p class=\"date\">March 03, 2026</p>\n <p>\n Serena's memory system has been significantly extended in functionality.\n It keeps the simplicity that made it popular but now supports structuring memories\n into topics and sharing them across projects.\n </p>\n <p>\n For more detailed information, see the <a href=\"https://oraios.github.io/serena/02-usage/045_memories.html\">documentation</a>.\n </p>\n</div>",
"20260321": "<div class=\"news-item\">\n <h3>Progressive Shortening and General Improvements in Tool Responses</h3>\n <p class=\"date\">March 21, 2026</p>\n <p>\n Tools that may deliver too long results now output informative shorter\n messages instead of just stating \"result is too long\". The mechanism uses\n progressive shortening so that the contained information is optimized\n for the length limit.\n </p>\n <p>\n In addition, several tool responses have been optimized without losing\n content, making Serena even more token efficient in general.\n </p>\n</div>",
"20260330": "<div class=\"news-item\">\n <h3>Querying of External Projects</h3>\n <p class=\"date\">March 30, 2026</p>\n <p>\n We added a new tool for querying external projects that are\n known to Serena: From the current project, use Serena's tools on another project to retrieve information.\n This is useful in situations where the current project builds on or is otherwise related\n to other projects.\n </p>\n <p>\n Find out more about this feature in our <a href=\"https://oraios.github.io/serena/02-usage/040_workflow.html#reading-from-external-projects\">documentation</a>.\n </p>\n</div>",
"20260404": "<div class=\"news-item\">\n <h3>Serena v1.0 Is Here!</h3>\n <p class=\"date\">April 3, 2026</p>\n <p>\n After a full year of work since the first version of Serena, we are excited to announce the release of Serena v1.0!\n </p>\n <p>\n Since our last update, we have now added support for <b>exciting new retrieval and refactoring tools</b>, especially for users of the JetBrains backend.\n </p>\n <ul>\n <li>safe_delete, jet_brains_safe_delete (beta):<br> Safely deletes a symbol, checking for remaining usages first</li>\n <li>jet_brains_move (beta):<br> Moves a symbol, file or directory to a new location, updating all references</li>\n <li>jet_brains_find_declaration:<br> Finds the declaration of a symbol using the JetBrains backend</li>\n <li>jet_brains_find_implementations:<br> Finds the implementations of a symbol (e.g. abstract method)</li>\n <li>jet_brains_inline_symbol (beta):<br> Inlines a symbol, replacing all call sites with the symbol’s body</li>\n </ul>\n <p>\n We are far from done. Many new features will be coming your way – for both the LSP and JetBrains backends.\n </p>\n <p>\n Thank you to our contributors, users and customers!\n </p>\n</div>"
}
+1 -3
View File
@@ -36,7 +36,7 @@ dependencies = [
"tiktoken==0.12.0",
"anthropic==0.59.0",
"beautifulsoup4==4.14.2",
"pywebview",
"oraios-pywebview==6.2",
"pystray==0.19.5",
# Transitive deps pinned for security (dependabot alerts).
# Exact pins because uvx installs from git, ignoring the lock file.
@@ -50,8 +50,6 @@ dependencies = [
"pythonnet==3.1.0-rc0 ; sys_platform == 'win32'",
]
[tool.uv.sources]
pywebview = { git = "https://github.com/r0x0r/pywebview", rev = "345596c1f4fd59fd26c7aa02d4176c5362bcbabe" }
[[tool.uv.index]]
name = "testpypi"
+11
View File
@@ -9,6 +9,7 @@ This reads all .html files from the `news/` directory and creates
"""
import json
import os
import sys
from pathlib import Path
@@ -41,4 +42,14 @@ def build_news_json() -> None:
if __name__ == "__main__":
deploy = False
for arg in sys.argv[1:]:
if arg == "--deploy":
deploy = True
build_news_json()
if deploy:
user = os.getenv("HADES_USER")
assert user, "HADES_USER environment variable must be set to deploy news.json to Hades"
os.system(f"scp news/news.json {user}@hades:/var/www/html/oraios-software/serena_news.json")
+7
View File
@@ -894,6 +894,13 @@ class SerenaAgent:
"""
return self._active_tools.contains_tool_name(tool_name)
def tool_is_exposed(self, tool_name: str) -> bool:
"""
:param tool_name: the name of the tool to check
:return: True if the tool is in the exposed tool set, False otherwise
"""
return self._exposed_tools.contains_tool_name(tool_name)
def get_current_config_overview(self) -> str:
"""
:return: a string overview of the current configuration, including the active and available configuration options
+1 -1
View File
@@ -630,7 +630,7 @@ class SerenaDashboardAPI:
# ===== Remote News Methods =====
# The branch from which news are fetched. Change to a feature branch for testing.
_NEWS_JSON_URL = "https://raw.githubusercontent.com/oraios/serena/main/news/news.json"
_NEWS_JSON_URL = "https://oraios-software.de/serena_news.json"
def _fetch_news(self) -> None:
"""Fetch news.json from GitHub using ETag-based caching and store in memory. Silently ignores network errors."""
+29 -20
View File
@@ -103,11 +103,13 @@ class PreToolUseRemindAboutSerenaHook(PreToolUseHook):
increment nor reset counters, so they also do not mask bursts by pushing the last
timestamp forward.
Deny emissions are additionally rate-limited to at most one per
:attr:`ToolUseCounter._MIN_DENY_INTERVAL_SECONDS` (one minute): once a deny has
been emitted, subsequent bursts detected within that window still reset the
counters but do not produce further nudges, so the agent is not swamped with
reminders during a sustained non-symbolic-tool burst.
The hook is additionally gated by :attr:`ToolUseCounter._MIN_DENY_INTERVAL_SECONDS`
(two minutes by default): once a deny has been emitted, *every* subsequent
invocation of this hook is a no-op until the window has elapsed — neither the
counters are updated nor any further deny is produced. This prevents the agent
from being nudged more than once per window during a sustained non-symbolic-tool
burst, and also avoids surprising the user with reminders that were already
counted up under stale state.
"""
@dataclass
@@ -124,8 +126,9 @@ class PreToolUseRemindAboutSerenaHook(PreToolUseHook):
# reset period for the combined counter
_NON_SYMBOLIC_RESET_PERIOD_SECONDS = 2000
# minimum seconds between two emitted denies; rate-limits reminder nudges so
# a single sustained burst triggers at most one deny per minute
# minimum seconds between two engagements of the hook after a deny; the entire
# hook (counter updates included) is a no-op while this window is active, so a
# single sustained burst triggers at most one nudge per window
_MIN_DENY_INTERVAL_SECONDS = 120
n_recent_read_file_uses: int = 0
@@ -147,13 +150,13 @@ class PreToolUseRemindAboutSerenaHook(PreToolUseHook):
def too_many_recent_non_symbolic(self) -> bool:
return self.n_recent_non_symbolic_uses >= self._NON_SYMBOLIC_USES_THRESHOLD
def can_emit_deny(self, now: datetime) -> bool:
""":return: whether enough time has elapsed since the last emitted deny
to allow another one. A value of ``True`` is also returned when no
deny has been emitted yet in this session. The rate limit prevents
the agent from being nudged more than once per
:attr:`_MIN_DENY_INTERVAL_SECONDS`, even if further bursts are
detected in the meantime.
def is_hook_active(self, now: datetime) -> bool:
""":return: whether the hook should engage at all at ``now``. Returns
``False`` while we are still within :attr:`_MIN_DENY_INTERVAL_SECONDS`
of the most recent emitted deny — in that case the entire hook is
short-circuited (no counter updates, no deny). Returns ``True`` when
no deny has been emitted yet in this session, or when the window has
elapsed.
"""
if self.last_deny_timestamp is None:
return True
@@ -258,6 +261,13 @@ class PreToolUseRemindAboutSerenaHook(PreToolUseHook):
return any(verb in name for verb in self._READ_FILE_VERB_SUBSTRINGS)
def execute(self) -> None:
# gate the entire hook on the rate-limit window: while we are within
# _MIN_DENY_INTERVAL_SECONDS of the last emitted deny, no counter
# updates and no deny detection happen at all. The pickle is left
# untouched so state survives the gating window unchanged.
if not self._tool_call_counter.is_hook_active(self.triggered_at_timestamp):
return
self._tool_call_counter.update(self)
# pick the deny that matches the current call first, so the emitted message lines up
@@ -282,13 +292,12 @@ class PreToolUseRemindAboutSerenaHook(PreToolUseHook):
output_data = self._build_non_symbolic_deny()
if output_data is not None:
# reset first so burst detection starts fresh regardless of whether the
# deny is actually emitted; the rate limit only gates *emission*, not
# detection
# reset burst counters so the next interval starts fresh, then record
# the deny timestamp and emit. The is_hook_active() guard at the top
# ensures we never reach this branch within the rate-limit window.
self._tool_call_counter.reset()
if self._tool_call_counter.can_emit_deny(self.triggered_at_timestamp):
self._tool_call_counter.last_deny_timestamp = self.triggered_at_timestamp
click.echo(output_data.to_json_string())
self._tool_call_counter.last_deny_timestamp = self.triggered_at_timestamp
click.echo(output_data.to_json_string())
self._tool_call_counter.save(self)
def _build_grep_deny(self) -> "PreToolUseHook.OutputData":
@@ -4,11 +4,12 @@ prompt: |
as well as shell commands are handled by your own, internal tools.
If Serena's tools can be used to achieve your task, you should prioritize them.
**IMPORTANT**: If you haven't seen Serena's tools yet, you should use the tool search tool to explore them.
Avoid reading entire source code files unless it is strictly necessary!
**IMPORTANT**: If you haven't seen Serena's tools yet, you should use the tool search tool to load all of them
right now.
For exploring and reading code in a token-efficient manner, use Serena's code-centric overview and symbolic search tools.
For edits, where it makes sense, use Serena's editing tools since they are more token efficient.
Use your own editing tools only if Serena's editing tools would be suboptimal.
**IMPORTANT**: Never use a tool before reading its schema via the tool-search tool!
excluded_tools:
@@ -18,6 +19,7 @@ excluded_tools:
- replace_content
- find_file
- list_dir
- search_for_pattern
tool_description_overrides: {}
+12 -5
View File
@@ -4,7 +4,7 @@ Tools supporting the general workflow of the agent
import platform
from serena.tools import Tool, ToolMarkerDoesNotRequireActiveProject
from serena.tools import ReadMemoryTool, Tool, ToolMarkerDoesNotRequireActiveProject, WriteMemoryTool
class CheckOnboardingPerformedTool(Tool):
@@ -17,12 +17,16 @@ class CheckOnboardingPerformedTool(Tool):
Checks whether project onboarding was already performed.
You should always call this tool before beginning to actually work on the project/after activating a project.
"""
read_memory_tool_available = self.agent.tool_is_exposed(ReadMemoryTool.get_name_from_cls())
perform_onboarding_tool_available = self.agent.tool_is_exposed(OnboardingTool.get_name_from_cls())
if not read_memory_tool_available:
return "Memory reading tool not activated, skipping onboarding check."
project_memories = self.memories_manager.list_project_memories()
if len(project_memories) == 0:
msg = (
"Onboarding not performed yet (no memories available). "
"You should perform onboarding by calling the `onboarding` tool before proceeding with the task. "
)
msg = "Onboarding not performed yet (no memories available). "
if perform_onboarding_tool_available:
msg += "You should perform onboarding by calling the `onboarding` tool before proceeding with the task. "
else:
# Not reporting the list of memories here, as they were already reported at project activation
# (with the system prompt if the project was activated at startup)
@@ -46,6 +50,9 @@ class OnboardingTool(Tool):
:return: instructions on how to create the onboarding information
"""
write_memory_tool_available = self.agent.tool_is_exposed(WriteMemoryTool.get_name_from_cls())
if not write_memory_tool_available:
return "Memory writing tool not activated, skipping onboarding."
system = platform.system()
return self.prompt_factory.create_onboarding_prompt(system=system)
+24 -13
View File
@@ -180,8 +180,10 @@ class TestPreToolUseRemindAboutSerenaHook:
assert capsys.readouterr().out == ""
def test_rate_limit_suppresses_second_deny_within_interval(self, tmp_path: Path, capsys: pytest.CaptureFixture[str]):
"""A second burst detected within the minimum deny interval must not emit a deny."""
def test_rate_limit_gates_entire_hook_within_interval(self, tmp_path: Path, capsys: pytest.CaptureFixture[str]):
"""While within the rate-limit window, the entire hook must be a no-op:
no deny is emitted, AND the persisted counters must remain untouched.
"""
# first burst: should emit a deny
for _ in range(ToolUseCounter._GREP_USES_THRESHOLD):
with patch("sys.stdin", _make_stdin(_base_input("grep"))), patch("serena.hooks.serena_home_dir", str(tmp_path)):
@@ -189,13 +191,22 @@ class TestPreToolUseRemindAboutSerenaHook:
first_output = capsys.readouterr().out.strip()
assert first_output, "first burst should have emitted a deny"
# second burst immediately after: within the 1-minute window, must be suppressed
# snapshot the persisted counter immediately after the deny was emitted
stub_for_path = object.__new__(PreToolUseRemindAboutSerenaHook)
stub_for_path.session_persistence_dir = str(tmp_path / "hook_data" / _base_input()["session_id"])
counter_before = ToolUseCounter.load(stub_for_path)
# second burst immediately after: within the rate-limit window, the entire
# hook must short-circuit — no deny output and no counter mutation
for _ in range(ToolUseCounter._GREP_USES_THRESHOLD):
with patch("sys.stdin", _make_stdin(_base_input("grep"))), patch("serena.hooks.serena_home_dir", str(tmp_path)):
PreToolUseRemindAboutSerenaHook(HookClient.CLAUDE_CODE).execute()
assert capsys.readouterr().out == ""
counter_after = ToolUseCounter.load(stub_for_path)
assert counter_after == counter_before, "gated hook must not mutate the persisted counter"
def test_rate_limit_allows_deny_after_interval(self, tmp_path: Path, capsys: pytest.CaptureFixture[str]):
"""Once the minimum deny interval has elapsed, a fresh burst emits a deny again."""
# first burst: emits a deny
@@ -348,23 +359,23 @@ class TestToolUseCounter:
loaded = ToolUseCounter.load(hook_stub) # type: ignore[arg-type]
assert loaded == ToolUseCounter()
def test_can_emit_deny_respects_min_interval(self):
""":meth:`can_emit_deny` returns False within the minimum interval, True outside it."""
def test_is_hook_active_respects_min_interval(self):
""":meth:`is_hook_active` returns False within the minimum interval, True outside it."""
counter = ToolUseCounter()
base = datetime.now()
# no prior deny → always allowed
assert counter.can_emit_deny(base)
# no prior deny → hook is always active
assert counter.is_hook_active(base)
# within the interval → rate-limited
# within the interval → hook gated
counter.last_deny_timestamp = base
interval = ToolUseCounter._MIN_DENY_INTERVAL_SECONDS
assert not counter.can_emit_deny(base + timedelta(seconds=interval - 1))
assert not counter.can_emit_deny(base)
assert not counter.is_hook_active(base + timedelta(seconds=interval - 1))
assert not counter.is_hook_active(base)
# at/after the interval → allowed again
assert counter.can_emit_deny(base + timedelta(seconds=interval))
assert counter.can_emit_deny(base + timedelta(seconds=interval + 1))
# at/after the interval → hook active again
assert counter.is_hook_active(base + timedelta(seconds=interval))
assert counter.is_hook_active(base + timedelta(seconds=interval + 1))
def test_reset_preserves_last_deny_timestamp(self):
"""``reset`` clears burst counters but must keep ``last_deny_timestamp`` intact."""
Generated
+24 -20
View File
@@ -1685,6 +1685,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
]
[[package]]
name = "oraios-pywebview"
version = "6.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "bottle" },
{ name = "proxy-tools" },
{ name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-uniformtypeidentifiers", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-webkit", marker = "sys_platform == 'darwin'" },
{ name = "pythonnet", marker = "sys_platform == 'win32'" },
{ name = "qtpy", marker = "sys_platform == 'openbsd6'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0d/e4/6820d2a00b42621240762947c9e07d83acbb1251575349472a48fe9b1a9f/oraios_pywebview-6.2.tar.gz", hash = "sha256:2b176547321ff9c289ebb10d5b2423bc8887a4fc730b35a78656de7839e5be37", size = 499560, upload-time = "2026-04-10T15:01:42.363Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/76/ff666b5abc3b3ccc35f69d62b27290b75b7ef5ac4beb2c59e8f59f5da5ef/oraios_pywebview-6.2-py3-none-any.whl", hash = "sha256:c5b81d2f6cf4cbfdf3d608d5bc3e76d8f0a732dbdd8567660a9bc9cebdd35b84", size = 512739, upload-time = "2026-04-10T15:01:43.689Z" },
]
[[package]]
name = "overrides"
version = "7.7.0"
@@ -2412,24 +2434,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1a/3d/a378aa74085fc6f72923d5e39fc30efbc97541cfd49b3fe0a35f20d09ff7/pythonnet-3.1.0rc0-py3-none-any.whl", hash = "sha256:286ccf23d08e1cc2b5200bb91d0ee649436d2e8386fec36a85193c66d0ff8b5b", size = 293150, upload-time = "2026-02-25T10:46:48.535Z" },
]
[[package]]
name = "pywebview"
version = "6.2.dev50+g345596c1f"
source = { git = "https://github.com/r0x0r/pywebview?rev=345596c1f4fd59fd26c7aa02d4176c5362bcbabe#345596c1f4fd59fd26c7aa02d4176c5362bcbabe" }
dependencies = [
{ name = "bottle" },
{ name = "proxy-tools" },
{ name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-uniformtypeidentifiers", marker = "sys_platform == 'darwin'" },
{ name = "pyobjc-framework-webkit", marker = "sys_platform == 'darwin'" },
{ name = "pythonnet", marker = "sys_platform == 'win32'" },
{ name = "qtpy", marker = "sys_platform == 'openbsd6'" },
{ name = "typing-extensions" },
]
[[package]]
name = "pywin32"
version = "311"
@@ -2921,6 +2925,7 @@ dependencies = [
{ name = "jinja2" },
{ name = "joblib" },
{ name = "mcp" },
{ name = "oraios-pywebview" },
{ name = "overrides" },
{ name = "pathspec" },
{ name = "psutil" },
@@ -2930,7 +2935,6 @@ dependencies = [
{ name = "python-dotenv" },
{ name = "python-multipart" },
{ name = "pythonnet", marker = "sys_platform == 'win32'" },
{ name = "pywebview" },
{ name = "pyyaml" },
{ name = "regex" },
{ name = "requests" },
@@ -2998,6 +3002,7 @@ requires-dist = [
{ name = "mypy", marker = "extra == 'dev'", specifier = "==1.17.0" },
{ name = "nbconvert", marker = "extra == 'dev'", specifier = "==7.17.0" },
{ name = "nbsphinx", marker = "extra == 'dev'", specifier = "==0.9.7" },
{ name = "oraios-pywebview", specifier = "==6.2" },
{ name = "overrides", specifier = "==7.7.0" },
{ name = "pathspec", specifier = "==0.12.1" },
{ name = "poethepoet", marker = "extra == 'dev'", specifier = "==0.36.0" },
@@ -3014,7 +3019,6 @@ requires-dist = [
{ name = "python-dotenv", specifier = "==1.2.1" },
{ name = "python-multipart", specifier = "==0.0.22" },
{ name = "pythonnet", marker = "sys_platform == 'win32'", specifier = "==3.1.0rc0" },
{ name = "pywebview", git = "https://github.com/r0x0r/pywebview?rev=345596c1f4fd59fd26c7aa02d4176c5362bcbabe" },
{ name = "pyyaml", specifier = "==6.0.2" },
{ name = "regex", specifier = "==2026.2.28" },
{ name = "requests", specifier = "==2.33.0" },