From ab5dcceb4591ceb8edae3e1f5190af2de17417cf Mon Sep 17 00:00:00 2001 From: shivamuppal2318 <147269863+shivamuppal2318@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:16:45 +0530 Subject: [PATCH] fixed the cli issue --- meshmind/cli/plugin_stubs/__init__.py | 1 + .../cli/plugin_stubs/demo_review/plugin.yaml | 7 ++ .../demo_review/prompts/system_prompt.txt | 2 + .../plugin_stubs/demo_security/plugin.yaml | 7 ++ .../demo_security/prompts/system_prompt.txt | 2 + meshmind/cli/presets/__init__.py | 1 + meshmind/cli/presets/code.yaml | 37 ++++++++++ meshmind/cli/repl.py | 22 +++++- meshmind/plugins/loader.py | 72 +++++++++++-------- meshmind/sdk/mesh.py | 35 +++++---- meshmind/sdk/node_def.py | 7 +- pyproject.toml | 13 ++-- test-mesh/.meshmind/repl_history | 1 + test-mesh/meshmind.yaml | 37 ++++++++++ 14 files changed, 191 insertions(+), 53 deletions(-) create mode 100644 meshmind/cli/plugin_stubs/__init__.py create mode 100644 meshmind/cli/plugin_stubs/demo_review/plugin.yaml create mode 100644 meshmind/cli/plugin_stubs/demo_review/prompts/system_prompt.txt create mode 100644 meshmind/cli/plugin_stubs/demo_security/plugin.yaml create mode 100644 meshmind/cli/plugin_stubs/demo_security/prompts/system_prompt.txt create mode 100644 meshmind/cli/presets/__init__.py create mode 100644 meshmind/cli/presets/code.yaml create mode 100644 test-mesh/.meshmind/repl_history create mode 100644 test-mesh/meshmind.yaml diff --git a/meshmind/cli/plugin_stubs/__init__.py b/meshmind/cli/plugin_stubs/__init__.py new file mode 100644 index 0000000..655174b --- /dev/null +++ b/meshmind/cli/plugin_stubs/__init__.py @@ -0,0 +1 @@ +"""Bundled demo plugins shipped with MeshMind.""" diff --git a/meshmind/cli/plugin_stubs/demo_review/plugin.yaml b/meshmind/cli/plugin_stubs/demo_review/plugin.yaml new file mode 100644 index 0000000..50e9028 --- /dev/null +++ b/meshmind/cli/plugin_stubs/demo_review/plugin.yaml @@ -0,0 +1,7 @@ +id: demo_review +name: Demo Review +version: "1.0.0" +capabilities: + - code_review + - review + - analysis diff --git a/meshmind/cli/plugin_stubs/demo_review/prompts/system_prompt.txt b/meshmind/cli/plugin_stubs/demo_review/prompts/system_prompt.txt new file mode 100644 index 0000000..e776b89 --- /dev/null +++ b/meshmind/cli/plugin_stubs/demo_review/prompts/system_prompt.txt @@ -0,0 +1,2 @@ +Review code changes with emphasis on correctness, regressions, and missing tests. +State concrete findings first and keep summaries brief. diff --git a/meshmind/cli/plugin_stubs/demo_security/plugin.yaml b/meshmind/cli/plugin_stubs/demo_security/plugin.yaml new file mode 100644 index 0000000..3e4e24e --- /dev/null +++ b/meshmind/cli/plugin_stubs/demo_security/plugin.yaml @@ -0,0 +1,7 @@ +id: demo_security +name: Demo Security +version: "1.0.0" +capabilities: + - security + - threat_modeling + - analysis diff --git a/meshmind/cli/plugin_stubs/demo_security/prompts/system_prompt.txt b/meshmind/cli/plugin_stubs/demo_security/prompts/system_prompt.txt new file mode 100644 index 0000000..0a731ba --- /dev/null +++ b/meshmind/cli/plugin_stubs/demo_security/prompts/system_prompt.txt @@ -0,0 +1,2 @@ +Review systems for security flaws, unsafe configuration, data exposure, and privilege boundaries. +Prioritize exploitable issues and concrete mitigations. diff --git a/meshmind/cli/presets/__init__.py b/meshmind/cli/presets/__init__.py new file mode 100644 index 0000000..35bfcab --- /dev/null +++ b/meshmind/cli/presets/__init__.py @@ -0,0 +1 @@ +"""Packaged ``meshmind init`` preset templates.""" diff --git a/meshmind/cli/presets/code.yaml b/meshmind/cli/presets/code.yaml new file mode 100644 index 0000000..b0dac5b --- /dev/null +++ b/meshmind/cli/presets/code.yaml @@ -0,0 +1,37 @@ +mesh: + name: "{{mesh_name}}" + discovery: "mdns" + +defaults: + model: "gemma3:1b" + timeout: 120 + retries: 1 + +nodes: + {{mesh_name}}-reviewer: + type: "specialist" + port: 8401 + plugin: "demo_review" + system_prompt: | + You review code changes with a focus on bugs, regressions, and missing tests. + capabilities: + - "code_review" + - "review" + - "analysis" + + {{mesh_name}}-security: + type: "specialist" + port: 8402 + plugin: "demo_security" + system_prompt: | + You analyze applications for security risks, unsafe defaults, and hardening gaps. + capabilities: + - "security" + - "threat_modeling" + - "analysis" + + {{mesh_name}}-coordinator: + type: "coordinator" + port: 8403 + ui: true + ui_port: 8080 diff --git a/meshmind/cli/repl.py b/meshmind/cli/repl.py index f66d1d5..ee693b7 100644 --- a/meshmind/cli/repl.py +++ b/meshmind/cli/repl.py @@ -18,6 +18,7 @@ import asyncio import logging +from pathlib import Path from typing import TYPE_CHECKING import click @@ -50,7 +51,25 @@ async def _read_line(prompt: str) -> str | None: return None -async def run_mesh_repl(mesh: Mesh, *, base_url: str, stop: asyncio.Event) -> None: +def _append_history(history_path: Path | None, line: str) -> None: + """Persist one entered line if history storage is configured.""" + if history_path is None or not line: + return + try: + history_path.parent.mkdir(parents=True, exist_ok=True) + with history_path.open("a", encoding="utf-8") as f: + f.write(line + "\n") + except OSError: + logger.debug("Could not write REPL history to %s", history_path, exc_info=True) + + +async def run_mesh_repl( + mesh: Mesh, + *, + base_url: str, + stop: asyncio.Event, + repl_history_path: Path | None = None, +) -> None: """Block until the user types `stop` / `exit` or EOF; sets ``stop`` when exiting.""" click.echo() click.echo( @@ -68,6 +87,7 @@ async def run_mesh_repl(mesh: Mesh, *, base_url: str, stop: asyncio.Event) -> No line = line.strip() if not line: continue + _append_history(repl_history_path, line) parts = line.split(None, 1) cmd = parts[0].lower() if parts else "" diff --git a/meshmind/plugins/loader.py b/meshmind/plugins/loader.py index 905fe15..6c827bf 100644 --- a/meshmind/plugins/loader.py +++ b/meshmind/plugins/loader.py @@ -16,29 +16,39 @@ from __future__ import annotations -import importlib -import logging -from pathlib import Path -from typing import Any +import importlib +import logging +import os +from pathlib import Path +from typing import Any import yaml -logger = logging.getLogger(__name__) - -PLUGIN_DIR = Path.home() / ".meshmind" / "plugins" - - -def discover_plugins() -> list[dict[str, Any]]: - """Discover installed plugins in ~/.meshmind/plugins/. - - Returns list of plugin metadata dicts. - """ - plugins: list[dict[str, Any]] = [] - - if not PLUGIN_DIR.exists(): - return plugins - - for plugin_path in PLUGIN_DIR.iterdir(): +logger = logging.getLogger(__name__) + +DEFAULT_PLUGIN_DIR = Path.home() / ".meshmind" / "plugins" + + +def get_plugin_dir() -> Path: + """Return the active plugin root, honoring ``MESHMIND_PLUGIN_DIR``.""" + raw = os.environ.get("MESHMIND_PLUGIN_DIR", "").strip() + if raw: + return Path(raw).expanduser() + return DEFAULT_PLUGIN_DIR + + +def discover_plugins() -> list[dict[str, Any]]: + """Discover installed plugins in ~/.meshmind/plugins/. + + Returns list of plugin metadata dicts. + """ + plugins: list[dict[str, Any]] = [] + plugin_dir = get_plugin_dir() + + if not plugin_dir.exists(): + return plugins + + for plugin_path in plugin_dir.iterdir(): if not plugin_path.is_dir(): continue @@ -65,12 +75,14 @@ def load_plugin(name: str) -> dict[str, Any]: 1. ~/.meshmind/plugins// 2. Installed Python packages named meshmind_ - Returns config dict with: system_prompt, functions, function_handlers, capabilities. - """ - # Try local plugin directory first - plugin_path = PLUGIN_DIR / name - if plugin_path.exists(): - return _load_plugin_from_dir(plugin_path) + Returns config dict with: system_prompt, functions, function_handlers, capabilities. + """ + plugin_dir = get_plugin_dir() + + # Try local plugin directory first + plugin_path = plugin_dir / name + if plugin_path.exists(): + return _load_plugin_from_dir(plugin_path) # Try installed Python package (plugin names are sanitized to alphanumeric + underscore) sanitized = "".join(c if c.isalnum() or c == "_" else "_" for c in name) @@ -83,9 +95,9 @@ def load_plugin(name: str) -> dict[str, Any]: except ImportError: pass - raise FileNotFoundError( - f"Plugin '{name}' not found in {PLUGIN_DIR} or as a Python package" - ) + raise FileNotFoundError( + f"Plugin '{name}' not found in {plugin_dir} or as a Python package" + ) def _load_plugin_from_dir(plugin_path: Path) -> dict[str, Any]: @@ -153,4 +165,4 @@ def _load_plugin_from_dir(plugin_path: Path) -> dict[str, Any]: return config -__all__ = ["PLUGIN_DIR", "discover_plugins", "load_plugin"] +__all__ = ["DEFAULT_PLUGIN_DIR", "discover_plugins", "get_plugin_dir", "load_plugin"] diff --git a/meshmind/sdk/mesh.py b/meshmind/sdk/mesh.py index ef8675b..264c000 100644 --- a/meshmind/sdk/mesh.py +++ b/meshmind/sdk/mesh.py @@ -23,10 +23,11 @@ from pathlib import Path from typing import Any, Callable -from meshmind.config.parser import load_mesh_config -from meshmind.core.node import MeshNode -from meshmind.core.protocol import NodeInfo -from meshmind.knowledge.loader import load_knowledge_files +from meshmind.config.parser import load_mesh_config +from meshmind.core.node import MeshNode +from meshmind.core.protocol import NodeInfo +from meshmind.knowledge.loader import load_knowledge_files +from meshmind.plugins.merge import apply_plugin_to_nodedef try: from meshmind.sdk.node_def import NodeDef @@ -41,8 +42,9 @@ class NodeDef: capabilities: list[str] = field(default_factory=list) knowledge_files: list[str] = field(default_factory=list) knowledge_domains: list[str] = field(default_factory=list) - ui: bool = False - ui_port: int = 8080 + ui: bool = False + ui_port: int = 8080 + plugin: str = "" def to_node_info(self) -> NodeInfo: return NodeInfo( @@ -159,19 +161,24 @@ def from_yaml(cls, path: str | Path) -> "Mesh": system_prompt=node_def.system_prompt, capabilities=list(node_def.capabilities), knowledge_files=list(node_def.knowledge), - knowledge_domains=list(node_def.knowledge_domains), - ui=node_def.ui, - ui_port=node_def.ui_port, - ) - mesh._node_defs.append(nd) + knowledge_domains=list(node_def.knowledge_domains), + ui=node_def.ui, + ui_port=node_def.ui_port, + plugin=node_def.plugin, + ) + if nd.plugin: + apply_plugin_to_nodedef(nd, nd.plugin) + mesh._node_defs.append(nd) return mesh def add_node(self, node_def: NodeDef) -> None: """Add a specialist node to the mesh.""" - if not node_def.model: - node_def.model = self.default_model - self._node_defs.append(node_def) + if not node_def.model: + node_def.model = self.default_model + if node_def.plugin: + apply_plugin_to_nodedef(node_def, node_def.plugin) + self._node_defs.append(node_def) def add_coordinator( self, diff --git a/meshmind/sdk/node_def.py b/meshmind/sdk/node_def.py index 783c5c8..31238f9 100644 --- a/meshmind/sdk/node_def.py +++ b/meshmind/sdk/node_def.py @@ -38,9 +38,10 @@ class NodeDef: knowledge_files: list[str] = field(default_factory=list) functions: list[dict] = field(default_factory=list) function_handlers: dict = field(default_factory=dict) - knowledge_domains: list[str] = field(default_factory=list) - ui: bool = False - ui_port: int = 8080 + knowledge_domains: list[str] = field(default_factory=list) + ui: bool = False + ui_port: int = 8080 + plugin: str = "" def to_node_info(self) -> NodeInfo: """Convert to a NodeInfo for the core layer.""" diff --git a/pyproject.toml b/pyproject.toml index 36a29ba..0537730 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,11 @@ include = ["meshmind*"] [tool.setuptools.package-data] meshmind = [ - "knowledge/*.json", - "ui/templates/*.html", - "ui/static/**/*", - "cli/templates/*.tmpl", -] + "knowledge/*.json", + "ui/templates/*.html", + "ui/static/**/*", + "cli/templates/*.tmpl", + "cli/presets/*.yaml", + "cli/plugin_stubs/*/plugin.yaml", + "cli/plugin_stubs/*/prompts/*.txt", +] diff --git a/test-mesh/.meshmind/repl_history b/test-mesh/.meshmind/repl_history new file mode 100644 index 0000000..45b983b --- /dev/null +++ b/test-mesh/.meshmind/repl_history @@ -0,0 +1 @@ +hi diff --git a/test-mesh/meshmind.yaml b/test-mesh/meshmind.yaml new file mode 100644 index 0000000..3ae8e5f --- /dev/null +++ b/test-mesh/meshmind.yaml @@ -0,0 +1,37 @@ +mesh: + name: "test-mesh" + discovery: "mdns" + +defaults: + model: "gemma3:1b" + timeout: 120 + retries: 1 + +nodes: + test-mesh-reviewer: + type: "specialist" + port: 8401 + plugin: "demo_review" + system_prompt: | + You review code changes with a focus on bugs, regressions, and missing tests. + capabilities: + - "code_review" + - "review" + - "analysis" + + test-mesh-security: + type: "specialist" + port: 8402 + plugin: "demo_security" + system_prompt: | + You analyze applications for security risks, unsafe defaults, and hardening gaps. + capabilities: + - "security" + - "threat_modeling" + - "analysis" + + test-mesh-coordinator: + type: "coordinator" + port: 8403 + ui: true + ui_port: 8080