pypi Python analyzed 0.9.0

willows-grove

v0.9.0
pypi

The operator's seat for the Willow AI fleet — loopback-only served page + MCP server

maintainer
Sean Campbell
licence
Apache-2.0
first seen
2026-08-27
last seen
2026-08-28
releases · 30d
1
short id

Drift inferred · capture-to-capture

No drift recorded — single capability capture; advisories appear once its surface changes.

capabilities 23 tools
transport stdio · streamable-http · http counts 23 tools · 0 res · 0 prompts permission surface via code analysis

tools

  • grove_ack
  • grove_agents
  • grove_bus_delete
  • grove_bus_receive
  • grove_bus_send
  • grove_create_channel
  • grove_flag
  • grove_flagged
  • grove_fleet_status
  • grove_get_history
  • grove_get_identity
  • grove_get_thread
  • grove_heartbeat
  • grove_human_required
  • grove_inbox
  • grove_list_channels
  • grove_mentions
  • grove_reply
  • grove_search
  • grove_send_message
  • grove_unflag
  • grove_watch
  • grove_watch_all
skills & danger signals pypi-sdist
prompt-surface shipped agent-instruction files + hidden-content / dangerous-code findings — quoted from the analyzed source

analyzed v0.9.0 · analyzer v33 · 2w ago

skills & prompt files 1

  • agent-rules willows_grove-0.9.0/CLAUDE.md

danger signals1

  • suspicious endpoint grove.example.ngrok.app willows_grove-0.9.0/tests/test_transport_security.py :45 "https://grove.example.ngrok.app",
code evidence v0.9.0 · pypi-sdist
evidence-backed findings quoted directly from the published source artifact — not inferred

code files: 129

filesystem 57

  • fs willows_grove-0.9.0/bridge/__main__.py :33 from pathlib import Path
  • fs willows_grove-0.9.0/bridge/app.py :25 from pathlib import Path
  • fs willows_grove-0.9.0/bridge/store.py :3 from pathlib import Path
  • fs willows_grove-0.9.0/grove/envelope_reader.py :43 from pathlib import Path
  • fs willows_grove-0.9.0/grove/mcp_auth.py :41 from pathlib import Path
  • fs willows_grove-0.9.0/grove/mcp_local.py :47 from pathlib import Path
  • fs willows_grove-0.9.0/grove/nestor_client.py :31 import shutil
  • fs willows_grove-0.9.0/grove/persona_roster.py :35 from pathlib import Path
  • fs willows_grove-0.9.0/grove/resident_watcher.py :53 from pathlib import Path
  • fs willows_grove-0.9.0/grove/seed_reader.py :37 from pathlib import Path
  • fs willows_grove-0.9.0/grove_serve.py :27 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/scripts/check_docs_drift.py :33 from pathlib import Path
show 28 more
  • fs (weak) willows_grove-0.9.0/scripts/check_persona_provenance.py :29 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/scripts/check_ratification.py :38 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/scripts/mcp_entry_toggle.py :21 with open(path) as f:
  • fs (weak) willows_grove-0.9.0/tests/e2e_ollama/conftest.py :50 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/e2e_ollama/test_watcher_ollama_e2e.py :34 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/e2e_willow_mcp/conftest.py :24 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_ci_hashfiles_guards_removed.py :42 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_ci_playwright_install_command.py :27 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_ci_security_grep_docstring_matches_pattern.py :21 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_claude_md_honesty.py :29 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_docs_drift_check.py :18 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_docs_no_dead_envvar_references.py :13 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_e2e_ollama_ci_fails_loud.py :36 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_envelope_reader.py :14 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_frank_ledger_error_surfaces.py :27 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_grove_approval_page.py :21 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_grove_chat_render.py :16 import pathlib
  • fs (weak) willows_grove-0.9.0/tests/test_grove_db_timeouts.py :25 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_grove_lens_switch.py :51 with open(component, "r", encoding="utf-8") as fh:
  • fs (weak) willows_grove-0.9.0/tests/test_grove_serve_env_validation.py :30 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_grove_serve_envelopes.py :23 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_grove_serve_personas.py :29 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_mcp_auth.py :12 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_mcp_local_oauth_hardening.py :28 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_mcp_serve_oauth_flow.py :12 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_nestor_client.py :7 import shutil
  • fs (weak) willows_grove-0.9.0/tests/test_panel_wiring.py :40 from pathlib import Path
  • fs (weak) willows_grove-0.9.0/tests/test_pending_ttl_hard_ceiling.py :12 from pathlib import Path

17 more not shown — this panel samples each group; the count above is the real total.

shell / exec 11

  • shell willows_grove-0.9.0/grove/mcp_local.py :1194 import subprocess
  • shell willows_grove-0.9.0/grove/nestor_client.py :32 import subprocess
  • shell willows_grove-0.9.0/grove_serve.py :24 import subprocess
  • shell (weak) willows_grove-0.9.0/scripts/check_persona_provenance.py :27 import subprocess
  • shell (weak) willows_grove-0.9.0/tests/test_ci_hashfiles_guards_removed.py :40 import subprocess
  • shell (weak) willows_grove-0.9.0/tests/test_ci_security_grep_docstring_matches_pattern.py :38 # - os.system( — arbitrary shell invocation
  • shell (weak) willows_grove-0.9.0/tests/test_docs_drift_check.py :16 import subprocess
  • shell (weak) willows_grove-0.9.0/tests/test_grove_serve_env_validation.py :28 import subprocess
  • shell (weak) willows_grove-0.9.0/tests/test_persona_provenance_check.py :14 import subprocess
  • shell (weak) willows_grove-0.9.0/tests/test_ratification_check.py :9 import subprocess
  • shell (weak) willows_grove-0.9.0/tests/test_refusal_summon_shape.py :34 import subprocess

network 25

  • net willows_grove-0.9.0/bridge/app.py :24 import socket
  • net willows_grove-0.9.0/bridge/matrix.py :4 from aiohttp import web, ClientSession, ClientTimeout
  • net willows_grove-0.9.0/grove/journal_reader.py :40 import urllib.error
  • net willows_grove-0.9.0/grove/journal_writer.py :34 import urllib.error
  • net willows_grove-0.9.0/grove/mcp_local.py :42 import socket
  • net willows_grove-0.9.0/grove/resident_watcher.py :51 import urllib.error
  • net (weak) willows_grove-0.9.0/tests/e2e_ollama/conftest.py :48 import urllib.error
  • net (weak) willows_grove-0.9.0/tests/e2e_ollama/test_watcher_ollama_readiness.py :21 import urllib.error
  • net (weak) willows_grove-0.9.0/tests/e2e_willow_mcp/conftest.py :18 import socket
  • net (weak) willows_grove-0.9.0/tests/e2e_willow_mcp/test_watcher_chat_readback_flow.py :54 # ``urllib.request.urlopen`` at the module level would also bleed
  • net (weak) willows_grove-0.9.0/tests/test_grove_approval_page.py :22 from urllib.parse import parse_qs, urlparse
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve.py :14 import socket
show 13 more
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_dispatch.py :15 import socket
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_envelopes.py :15 import socket
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_journal.py :16 import socket
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_journal_read.py :15 import socket
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_nestor.py :18 import socket
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_personas.py :21 import socket
  • net (weak) willows_grove-0.9.0/tests/test_grove_serve_seed.py :12 import socket
  • net (weak) willows_grove-0.9.0/tests/test_journal_reader.py :88 journal_reader.urllib.request, "urlopen",
  • net (weak) willows_grove-0.9.0/tests/test_journal_writer.py :109 journal_writer.urllib.request, "urlopen", _fake_urlopen
  • net (weak) willows_grove-0.9.0/tests/test_mcp_serve_oauth_flow.py :13 from urllib.parse import parse_qs, urlparse
  • net (weak) willows_grove-0.9.0/tests/test_panel_wiring.py :32 import socket
  • net (weak) willows_grove-0.9.0/tests/test_persona_roster_unreachable.py :28 import socket
  • net (weak) willows_grove-0.9.0/tests/test_seed_canon_content.py :23 import socket

database 14

  • db willows_grove-0.9.0/bridge/store.py :2 import sqlite3
  • db willows_grove-0.9.0/grove/kart_reader.py :232 import psycopg2 # local import — kart_reader is a leaf module
  • db willows_grove-0.9.0/grove/mcp_local.py :79 import psycopg2
  • db willows_grove-0.9.0/grove/resident_watcher.py :315 import psycopg2 # type: ignore
  • db willows_grove-0.9.0/grove_db.py :107 import psycopg2.pool
  • db willows_grove-0.9.0/grove_reader.py :65 import psycopg2
  • db (weak) willows_grove-0.9.0/tests/e2e_ollama/conftest.py :334 import psycopg2 # type: ignore
  • db (weak) willows_grove-0.9.0/tests/e2e_ollama/test_watcher_ollama_e2e.py :63 import psycopg2 # type: ignore
  • db (weak) willows_grove-0.9.0/tests/test_frank_ledger_error_surfaces.py :46 import psycopg2.errors
  • db (weak) willows_grove-0.9.0/tests/test_grove_db_cursor_unreachable.py :35 import psycopg2 # noqa: E402
  • db (weak) willows_grove-0.9.0/tests/test_grove_db_timeouts.py :72 import psycopg2
  • db (weak) willows_grove-0.9.0/tests/test_grove_reader_error_redaction.py :44 import psycopg2 # noqa: E402
show 2 more
  • db (weak) willows_grove-0.9.0/tests/test_grove_reader_unreachable.py :30 import psycopg2 # noqa: E402
  • db (weak) willows_grove-0.9.0/tests/test_kart_reader.py :46 import psycopg2

tool registrations 23

  • grove_list_channels willows_grove-0.9.0/grove/mcp_local.py :426
  • grove_get_history willows_grove-0.9.0/grove/mcp_local.py :441
  • grove_send_message willows_grove-0.9.0/grove/mcp_local.py :477
  • grove_search willows_grove-0.9.0/grove/mcp_local.py :500
  • grove_get_identity willows_grove-0.9.0/grove/mcp_local.py :529
  • grove_watch willows_grove-0.9.0/grove/mcp_local.py :608
  • grove_watch_all willows_grove-0.9.0/grove/mcp_local.py :632
  • grove_reply willows_grove-0.9.0/grove/mcp_local.py :662
  • grove_get_thread willows_grove-0.9.0/grove/mcp_local.py :688
  • grove_flag willows_grove-0.9.0/grove/mcp_local.py :716
  • grove_unflag willows_grove-0.9.0/grove/mcp_local.py :737
  • grove_bus_send willows_grove-0.9.0/grove/mcp_local.py :756
show 11 more
  • grove_bus_receive willows_grove-0.9.0/grove/mcp_local.py :799
  • grove_bus_delete willows_grove-0.9.0/grove/mcp_local.py :825
  • grove_ack willows_grove-0.9.0/grove/mcp_local.py :845
  • grove_heartbeat willows_grove-0.9.0/grove/mcp_local.py :877
  • grove_inbox willows_grove-0.9.0/grove/mcp_local.py :903
  • grove_flagged willows_grove-0.9.0/grove/mcp_local.py :922
  • grove_agents willows_grove-0.9.0/grove/mcp_local.py :970
  • grove_fleet_status willows_grove-0.9.0/grove/mcp_local.py :981
  • grove_mentions willows_grove-0.9.0/grove/mcp_local.py :997
  • grove_human_required willows_grove-0.9.0/grove/mcp_local.py :1013
  • grove_create_channel willows_grove-0.9.0/grove/mcp_local.py :1030

declared dependencies 9

  • mcp@>=2.0.0,<3.0.0
  • psycopg2-binary@>=2.9.0,<3
  • cryptography@>=42.0.0,<50
  • aiohttp@>=3.9.0,<4
  • starlette@>=0.37.0,<2
  • uvicorn@>=0.30.0,<1
  • textual@>=0.61.0,<9
  • pytest@>=8.0.0,<10
  • pytest-timeout@*

transport_posture 1

  • network-listening, binds all interfaces, no auth detected willows_grove-0.9.0/bridge/app.py :137 host="0.0.0.0", port=self._grove_port,