diff --git a/.tmp/sitecustomize.py b/.tmp/sitecustomize.py
new file mode 100644
index 0000000..07cb26a
--- /dev/null
+++ b/.tmp/sitecustomize.py
@@ -0,0 +1,8 @@
+import tempfile
+
+_real_mkdir = tempfile._os.mkdir
+
+def _mkdir(path, mode=0o777):
+ return _real_mkdir(path, 0o777)
+
+tempfile._os.mkdir = _mkdir
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..955f2b6
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,15 @@
+include README.md
+include LICENSE
+include CHANGELOG.md
+include CONTRIBUTING.md
+include requirements.txt
+
+recursive-include meshmind/knowledge *.json
+recursive-include meshmind/ui/template *.html
+recursive-include meshmind/ui/static *
+recursive-include meshmind/cli/templates *.tmpl
+recursive-include meshmind/cli/presets *.yaml
+recursive-include meshmind/cli/plugin_stubs *.yaml *.txt *.md
+
+prune meshmind.egg-info
+global-exclude __pycache__ *.py[cod]
diff --git a/PUBLISHING.md b/PUBLISHING.md
new file mode 100644
index 0000000..7b1caf9
--- /dev/null
+++ b/PUBLISHING.md
@@ -0,0 +1,36 @@
+# Publishing to PyPI
+
+## 1) Prep
+
+- Make sure `pyproject.toml` has the right `project.version`.
+- Update `CHANGELOG.md` (and optionally docs release notes).
+- Tag the release (recommended): `git tag vX.Y.Z`
+
+## 2) Build
+
+```bash
+python -m pip install -U build twine
+python -m build
+python -m twine check dist/*
+```
+
+## 3) Upload
+
+TestPyPI (recommended first):
+
+```bash
+python -m twine upload -r testpypi dist/*
+```
+
+PyPI:
+
+```bash
+python -m twine upload dist/*
+```
+
+## 4) Verify install
+
+```bash
+python -m pip install -U meshmind
+meshmind --help
+```
diff --git a/README.md b/README.md
index 9cc6294..9aa68a2 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
# MeshMind
@@ -28,7 +28,7 @@ This is not distributed inference for a single giant model. MeshMind is built ar
**Install (released package):** Python **3.11+** and [Ollama](https://ollama.com). Stable installs:
```bash
-pip install meshmind
+pip install -U meshmind
```
**Install (from source, contributors):** from the repo root:
diff --git a/docs/plugins.md b/docs/plugins.md
index 7e7291d..621c6ae 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -52,8 +52,8 @@ When `meshmind.yaml` sets `plugin: "
"` on a node:
## Bundled demos
-The repo ships stub plugins `demo_review` and `demo_security` under `meshmind/cli/plugin_stubs/`.
-`meshmind init --preset code` can copy them into your plugin directory (default unless `--skip-bundled-plugins`).
+The repo ships stub plugins `demo_review`, `demo_security`, `demo_documentation`, and `demo_research` under `meshmind/cli/plugin_stubs/`.
+`meshmind init --preset code` can copy `demo_review` and `demo_security` into your plugin directory (default unless `--skip-bundled-plugins`).
If you skipped that step or moved machines, run `meshmind plugin sync -c meshmind.yaml` before `meshmind up`.
## meshmind.yaml
diff --git a/meshmind/cli/__init__.py b/meshmind/cli/__init__.py
new file mode 100644
index 0000000..256192e
--- /dev/null
+++ b/meshmind/cli/__init__.py
@@ -0,0 +1 @@
+"""MeshMind CLI package."""
diff --git a/meshmind/cli/__inti__.py b/meshmind/cli/__inti__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/meshmind/cli/plugin_stubs/demo_documentation/plugin.yaml b/meshmind/cli/plugin_stubs/demo_documentation/plugin.yaml
new file mode 100644
index 0000000..ad1311e
--- /dev/null
+++ b/meshmind/cli/plugin_stubs/demo_documentation/plugin.yaml
@@ -0,0 +1,13 @@
+id: demo_documentation
+name: Demo Documentation
+capabilities:
+ - documentation
+ - writing
+ - editing
+functions:
+ - name: write_documentation
+ description: Generate clear documentation, user guides, or API reference.
+ parameters: {}
+ - name: improve_readability
+ description: Rewrite text to make it easier to understand and scan.
+ parameters: {}
diff --git a/meshmind/cli/plugin_stubs/demo_documentation/prompts/system_prompt.txt b/meshmind/cli/plugin_stubs/demo_documentation/prompts/system_prompt.txt
new file mode 100644
index 0000000..e08d807
--- /dev/null
+++ b/meshmind/cli/plugin_stubs/demo_documentation/prompts/system_prompt.txt
@@ -0,0 +1 @@
+You are a documentation specialist. Produce clear, concise guides, API reference, and examples with a reader-first approach.
diff --git a/meshmind/cli/plugin_stubs/demo_research/plugin.yaml b/meshmind/cli/plugin_stubs/demo_research/plugin.yaml
new file mode 100644
index 0000000..4a0d8a7
--- /dev/null
+++ b/meshmind/cli/plugin_stubs/demo_research/plugin.yaml
@@ -0,0 +1,13 @@
+id: demo_research
+name: Demo Research
+capabilities:
+ - research
+ - analysis
+ - summarization
+functions:
+ - name: research_topic
+ description: Investigate a topic and summarize key findings.
+ parameters: {}
+ - name: extract_insights
+ description: Extract the most important insights from a body of text.
+ parameters: {}
diff --git a/meshmind/cli/plugin_stubs/demo_research/prompts/system_prompt.txt b/meshmind/cli/plugin_stubs/demo_research/prompts/system_prompt.txt
new file mode 100644
index 0000000..1bcbfdf
--- /dev/null
+++ b/meshmind/cli/plugin_stubs/demo_research/prompts/system_prompt.txt
@@ -0,0 +1 @@
+You are a researcher assistant. Gather relevant information, analyze it critically, and summarize your findings in a useful way.
diff --git a/meshmind/config/schema.py b/meshmind/config/schema.py
index afa963f..9ac1a4c 100644
--- a/meshmind/config/schema.py
+++ b/meshmind/config/schema.py
@@ -59,6 +59,7 @@ class DefaultsConfig(BaseModel):
model: str = "gemma4"
timeout: int = 60
retries: int = 1
+ capability_keywords: dict[str, list[str]] = Field(default_factory=dict)
class PluginReference(BaseModel):
diff --git a/meshmind/core/node.py b/meshmind/core/node.py
index 124c1ea..6d2b68c 100644
--- a/meshmind/core/node.py
+++ b/meshmind/core/node.py
@@ -250,6 +250,7 @@ def __init__(
registry=self.registry,
ai_engine=self.ai_engine if use_ai else None,
event_logger=self._log_event,
+ capability_keywords=config.get("capability_keywords"),
)
self.discovery = MeshDiscovery(
diff --git a/meshmind/core/orchestrator.py b/meshmind/core/orchestrator.py
index a287fde..9b0f213 100644
--- a/meshmind/core/orchestrator.py
+++ b/meshmind/core/orchestrator.py
@@ -139,6 +139,7 @@ def __init__(
ai_engine=None,
max_retries: int = 0,
event_logger=None,
+ capability_keywords: dict[str, list[str]] | None = None,
) -> None:
self.node_id = node_id
self.node_name = node_name
@@ -148,6 +149,7 @@ def __init__(
self.event_logger = event_logger
self.traces: list[QueryTrace] = []
self._max_traces = 100
+ self._capability_keywords = capability_keywords
def _log_event(self, event_type: str, data: dict[str, Any]) -> None:
if callable(self.event_logger):
@@ -186,7 +188,7 @@ async def _classify_and_route(self, query: str) -> list[dict]:
except Exception:
logger.warning("AI classification failed, falling back to keywords")
- return build_routing_plan(self.registry, query, self.node_id)
+ return build_routing_plan(self.registry, query, self.node_id, self._capability_keywords)
async def process_query(self, query: str) -> tuple[str, QueryTrace]:
"""Process a user query by routing to specialized nodes and aggregating responses.
diff --git a/meshmind/sdk/mesh.py b/meshmind/sdk/mesh.py
index f9319e3..ef364ec 100644
--- a/meshmind/sdk/mesh.py
+++ b/meshmind/sdk/mesh.py
@@ -122,6 +122,7 @@ def __init__(
manual_peers: list[dict] | None = None,
default_model: str = "gemma4",
dev_mode: bool | None = None,
+ capability_keywords: dict[str, list[str]] | None = None,
) -> None:
self.name = name
self.discovery = discovery
@@ -138,6 +139,7 @@ def __init__(
self._running = False
self._event_handlers: dict[str, list[Callable]] = {}
self._config_dir: Path = Path(".")
+ self._capability_keywords = capability_keywords or {}
@classmethod
def from_yaml(cls, path: str | Path) -> "Mesh":
@@ -152,6 +154,7 @@ def from_yaml(cls, path: str | Path) -> "Mesh":
manual_peers=peers,
default_model=config.defaults.model,
dev_mode=None,
+ capability_keywords=config.defaults.capability_keywords,
)
mesh._config_dir = path.parent
@@ -235,6 +238,10 @@ async def start(self) -> None:
+ knowledge_context
)
+ # Add mesh-wide capability keywords to node config
+ if self._capability_keywords:
+ config["capability_keywords"] = self._capability_keywords
+
node = MeshNode(
node_info=node_info,
config=config,
diff --git a/my-project/.meshmind/repl_history b/my-project/.meshmind/repl_history
new file mode 100644
index 0000000..0d7fd40
--- /dev/null
+++ b/my-project/.meshmind/repl_history
@@ -0,0 +1,68 @@
+
+# 2026-04-20 11:27:03.556918
++python -m venv venv-test
++.\venv-test\Scripts\Activate.ps1
++python -m pip install --upgrade pip
++python -m pip install meshmind==0.1.1
++meshmind --version
+
+# 2026-04-20 12:19:55.876072
++ meshmind plugin install demo_documentation
++meshmind plugin install demo_research
+
+# 2026-04-20 12:32:16.012494
++hi
+
+# 2026-04-20 12:40:28.008274
++hiii which plugins are there
+
+# 2026-04-20 12:44:55.592211
++What tools do you have available?
+
+# 2026-04-20 12:50:44.647563
++Write a short guide on how to install Python packages
+
+# 2026-04-20 12:52:01.938719
++hi
+
+# 2026-04-20 12:52:13.162441
++
+
+# 2026-04-20 12:57:08.104379
++hi
+
+# 2026-04-20 12:58:16.572155
++What tools do you have available?
+
+# 2026-04-20 12:58:59.034871
++what is cg
+
+# 2026-04-20 12:59:10.187850
++what is dsa
+
+# 2026-04-20 12:59:22.030278
++dsa
+
+# 2026-04-20 13:00:39.975381
++what other plugins are there in the meshmind
+
+# 2026-04-20 13:03:03.884260
++Research the benefits of renewable energy
+
+# 2026-04-20 13:24:54.566450
++Research renewable energy AND write documentation about solar panels
+
+# 2026-04-20 13:27:04.002920
++hu
+
+# 2026-04-20 13:35:17.967465
++hi
+
+# 2026-04-20 13:36:03.873971
++Research renewable energy AND write documentation about solar panels
+
+# 2026-04-20 13:38:40.568903
++write a short poem
+
+# 2026-04-20 14:12:39.308336
++hi
diff --git a/my-project/meshmind.yaml b/my-project/meshmind.yaml
index 34316b8..7bd2cef 100644
--- a/my-project/meshmind.yaml
+++ b/my-project/meshmind.yaml
@@ -12,10 +12,39 @@ mesh:
# - host: "192.168.1.10"
# port: 8401
-defaults:
- model: 'gemma3:1b' # Lower-memory Gemma model for local development
+ defaults:
+ model: 'llama3.2:latest' # Model with tool support
timeout: 60 # Query timeout in seconds
retries: 1 # Retry failed sub-queries once
+ capability_keywords:
+ research:
+ - research
+ - investigate
+ - study
+ - analyze
+ - survey
+ analysis:
+ - analysis
+ - analyze
+ - examine
+ - evaluate
+ summarization:
+ - summary
+ - summarize
+ - brief
+ - concise
+ documentation:
+ - documentation
+ - document
+ - guide
+ - manual
+ - write
+ - writing
+ writing:
+ - write
+ - writing
+ - compose
+ - draft
nodes:
# ------------------------------------
@@ -23,7 +52,7 @@ nodes:
# ------------------------------------
assistant:
type: 'specialist'
- # model: "gemma3:1b" # Override default model
+ model: "llama3.2:latest"
port: 8401
system_prompt: |
You are a helpful assistant specializing in research and analysis.
@@ -38,21 +67,40 @@ nodes:
knowledge:
- ./data/knowledge.json
- # Add more specialist nodes as needed:
- # writer:
- # type: "specialist"
- # port: 8402
- # system_prompt: "You are a technical writer..."
- # capabilities:
- # - "writing"
- # - "documentation"
+ documentation_specialist:
+ type: 'specialist'
+ model: "llama3.2:latest"
+ port: 8402
+ plugin: demo_documentation
+ capabilities:
+ - 'documentation'
+ - 'writing'
+ knowledge_domains:
+ - 'documentation'
+ system_prompt: |
+ You are a documentation specialist. Use the documentation plugin to generate clear guides, examples, and API reference.
+
+ research_specialist:
+ type: 'specialist'
+ model: "llama3.2:latest"
+ port: 8405
+ plugin: demo_research
+ capabilities:
+ - 'research'
+ - 'analysis'
+ - 'summarization'
+ knowledge_domains:
+ - 'research'
+ system_prompt: |
+ You are a research specialist. Use the research plugin to investigate topics and summarize findings.
# ------------------------------------
# Coordinator - routes queries and aggregates
# ------------------------------------
coordinator:
type: 'coordinator'
+ model: 'llama3.2:latest'
port: 8403
ui: true # Enable web dashboard
- ui_port: 9000 # Dashboard at http://localhost:9080
+ ui_port: 9000 # Dashboard at http://localhost:9000
diff --git a/pyproject.toml b/pyproject.toml
index ff9412c..1e0274b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,54 +1,71 @@
-[project]
-name = "meshmind"
-version = "0.1.1"
-description = "Orchestrate local LLMs across devices with no cloud dependency"
-readme = "README.md"
-requires-python = ">=3.11"
-license = {text = "Apache-2.0"}
-classifiers = [
- "License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.11",
- "Programming Language :: Python :: 3.12",
-]
-
-dependencies = [
- "zeroconf>=0.131.0",
- "ollama>=0.4.0",
- "fastapi>=0.111.0",
- "uvicorn>=0.30.0",
- "websockets>=12.0",
- "pyyaml>=6.0",
- "pydantic>=2.7",
- "click>=8.1",
- "prompt-toolkit>=3.0.43",
- "rich>=13.7.0",
-]
-
-[project.optional-dependencies]
-dev = [
- "pytest>=8.0",
- "pytest-asyncio>=0.23",
- "build>=1.2.0",
-]
-mcp = [
- "mcp>=1.2.0",
-]
-
-[project.scripts]
-meshmind = "meshmind.cli.main:cli"
-
-[tool.setuptools.packages.find]
-include = ["meshmind*"]
-
-[tool.setuptools.package-data]
-meshmind = [
- "knowledge/*.json",
- "ui/template/*.html",
- "ui/static/**/*",
- "cli/templates/*.tmpl",
- "cli/presets/*.yaml",
- "cli/plugin_stubs/**/*.yaml",
- "cli/plugin_stubs/**/*.txt",
- "cli/plugin_stubs/**/*.md",
-]
+[build-system]
+requires = ["setuptools>=69", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "meshmind"
+version = "0.1.1"
+description = "Orchestrate local LLMs across devices with no cloud dependency"
+readme = "README.md"
+requires-python = ">=3.11"
+license = "Apache-2.0"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+]
+keywords = ["llm", "ollama", "fastapi", "local-first", "distributed-systems", "agent", "plugins"]
+authors = [{name = "Nexarion Distributed AI"}]
+
+dependencies = [
+ "zeroconf>=0.131.0",
+ "ollama>=0.4.0",
+ "fastapi>=0.111.0",
+ "uvicorn>=0.30.0",
+ "websockets>=12.0",
+ "pyyaml>=6.0",
+ "pydantic>=2.7",
+ "click>=8.1",
+ "prompt-toolkit>=3.0.43",
+ "rich>=13.7.0",
+]
+
+[project.optional-dependencies]
+dev = [
+ "pytest>=8.0",
+ "pytest-asyncio>=0.23",
+ "build>=1.2.0",
+ "twine>=5.0.0",
+]
+mcp = [
+ "mcp>=1.2.0",
+]
+
+[project.scripts]
+meshmind = "meshmind.cli.main:cli"
+
+[project.urls]
+Homepage = "https://github.com/Nexarion-Distributed-AI/MeshMind"
+Repository = "https://github.com/Nexarion-Distributed-AI/MeshMind"
+Issues = "https://github.com/Nexarion-Distributed-AI/MeshMind/issues"
+Changelog = "https://github.com/Nexarion-Distributed-AI/MeshMind/blob/main/CHANGELOG.md"
+Documentation = "https://github.com/Nexarion-Distributed-AI/MeshMind/tree/main/docs"
+
+[tool.setuptools]
+include-package-data = true
+license-files = ["LICENSE"]
+
+[tool.setuptools.packages.find]
+include = ["meshmind*"]
+
+[tool.setuptools.package-data]
+meshmind = [
+ "knowledge/*.json",
+ "ui/template/*.html",
+ "ui/static/**/*",
+ "cli/templates/*.tmpl",
+ "cli/presets/*.yaml",
+ "cli/plugin_stubs/**/*.yaml",
+ "cli/plugin_stubs/**/*.txt",
+ "cli/plugin_stubs/**/*.md",
+]
diff --git a/shivam/data/knowledge.json b/shivam/data/knowledge.json
new file mode 100644
index 0000000..f0073ce
--- /dev/null
+++ b/shivam/data/knowledge.json
@@ -0,0 +1,7 @@
+{
+ "project_name": "shivam",
+ "topics": [
+ "Replace with topic areas this mesh should emphasize."
+ ],
+ "conventions": "Short conventions and style notes editors can follow when answering (tone, citation, formatting)."
+}
diff --git a/shivam/meshmind.yaml b/shivam/meshmind.yaml
new file mode 100644
index 0000000..cf882ed
--- /dev/null
+++ b/shivam/meshmind.yaml
@@ -0,0 +1,58 @@
+# MeshMind Configuration
+# Docs: https://github.com/meshmind/meshmind
+#
+# Define your mesh of AI nodes below.
+# Run `meshmind up` to start all nodes.
+# Run `meshmind config validate` to check this file.
+
+mesh:
+ name: "shivam"
+ discovery: "mdns" # "mdns" (auto-detect peers) or "manual" (static list)
+ # peers: # Only needed for manual discovery
+ # - host: "192.168.1.10"
+ # port: 8401
+
+defaults:
+ model: "gemma3:1b" # Default model for all nodes (any Ollama model works)
+ timeout: 60 # Query timeout in seconds
+ retries: 1 # Retry failed sub-queries once
+
+nodes:
+ # ------------------------------------
+ # Specialist Node - handles a specific domain
+ # ------------------------------------
+ shivam-assistant:
+ type: "specialist"
+ # model: "gemma3:1b" # Override default model
+ port: 8401
+ system_prompt: |
+ You are a helpful assistant specializing in research and analysis.
+ Provide detailed, accurate, and well-structured answers.
+ capabilities:
+ - "research"
+ - "analysis"
+ - "summarization"
+ knowledge_domains:
+ - "research"
+ - "analysis"
+ knowledge:
+ - ./data/knowledge.json
+
+ # Add more specialist nodes as needed:
+ # writer:
+ # type: "specialist"
+ # port: 8402
+ # system_prompt: "You are a technical writer..."
+ # capabilities:
+ # - "writing"
+ # - "documentation"
+
+ # ------------------------------------
+ # Coordinator - routes queries and aggregates
+ # ------------------------------------
+ shivam-coordinator:
+ type: "coordinator"
+ port: 8403
+ ui: true # Enable web dashboard
+ ui_port: 8080 # Dashboard at http://localhost:8080
+
diff --git a/tests/test_plugin_init_ux.py b/tests/test_plugin_init_ux.py
index 467d54d..376bbc7 100644
--- a/tests/test_plugin_init_ux.py
+++ b/tests/test_plugin_init_ux.py
@@ -37,6 +37,8 @@ def test_iter_bundled_plugin_ids_includes_demos() -> None:
ids = iter_bundled_plugin_ids()
assert "demo_review" in ids
assert "demo_security" in ids
+ assert "demo_documentation" in ids
+ assert "demo_research" in ids
def test_install_one_bundled_plugin_copies_stub(tmp_path, monkeypatch) -> None:
@@ -114,6 +116,9 @@ def test_plugin_catalog_cli_lists_bundled(args: list[str]) -> None:
r = CliRunner().invoke(cli, args)
assert r.exit_code == 0
assert "demo_review" in r.output
+ assert "demo_security" in r.output
+ assert "demo_documentation" in r.output
+ assert "demo_research" in r.output
def test_apply_plugin_file_not_found_message_includes_sync_hint(tmp_path, monkeypatch) -> None: