Mind is a lightweight local-first personal AI assistant runtime.
It is designed to run on your own machine, use a local model through Ollama, read and write files only through controlled local boundaries, maintain persistent memory, and expose a safe permissioned tool system for extending the assistant with custom Python capabilities.
Mind is not meant to be a thin wrapper around an API. The goal is to build a local-first assistant architecture that stays understandable, extensible, inspectable, and safe.
Mind is currently a v0.3 command-line prototype with a package-based internal architecture.
Implemented:
mindmind doctormind inspectmind ask <prompt>mind ask <prompt> --files <workspace-relative-path> [more-files...]mind ask --uncensored <prompt>mind ask --tools <prompt>mind ask --tools --trace <prompt>mind filesmind chatmind chat --uncensoredmind chat --toolsmind chat --tools --tracemind agent <task>mind agent --trace <task>mind remember <text>mind memoriesmind memories --status <confirmed|auto_extracted|rejected|archived>mind forget <memory-id>mind memory confirm <memory-id>mind memory reject <memory-id>mind memory archive <memory-id>mind memory delete <memory-id>mind memory backfillmind toolsmind runsmind run show <run-id>mind uncensored <prompt>- Basic assistant identity prompt
- Config-driven local model settings
- Workspace-relative file access
- Safe workspace file reading
- Safe workspace file writing and appending
- Project-root-relative codebase reading tools
- Centralized context building
- SQLite-backed persistent memory
- Memory context injection into prompts
- Experimental automatic memory extraction during chat
- Semantic memory embedding backfill
- Bounded tool-using agent loop
- Tool-enabled chat history
- File-based agent run persistence
- Strict JSON agent protocol parsing
- One repair attempt for invalid agent protocol output
- Structured
ToolResultobjects - Tool registry for controlled Python capabilities
- Tool permission metadata
- Configurable tool permission enforcement
- Confirmation metadata for higher-impact tools
- Interactive confirmation callback for confirmed tools
- Workspace, codebase, memory, and internet tool modules
- Read-only external API tool example
- Project status and devlog tools
- Read-only Git status tool
- Agent trace/debug output
- Unit tests for CLI routing, context building, memory, workspace access, codebase access, prompt construction, agent behavior, project tools, tool execution, and tool permissions
- Python 3.11+
- Ollama running locally
- A configured local model available through Ollama
The default model is configured in:
configs/config.toml
From the project root:
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"On Windows PowerShell:
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e ".[dev]"Mind uses a TOML config file:
configs/config.toml
Example:
[assistant]
name = "Mind"
description = "A lightweight local-first personal AI assistant."
[paths]
workspace = "workspace"
database = "data/mind.db"
[model]
provider = "ollama"
base_url = "http://localhost:11434"
default = "qwen3-coder:30b"
cloud = "gpt-oss:120b-cloud"
uncensored = "oroboroslabs/qwen3.5-abliterated-47-4:latest"
small = "qwen2.5:1.5b"
[memory]
auto_extract = true
inject_context = true
max_relevant_memories = 8
min_similarity = 0.3
[embeddings]
provider = "ollama"
model = "nomic-embed-text"
enabled = true
[context]
max_workspace_chars = 12000
[project]
root = "."
[tools]
allow_external_read = true
allow_local_write = true
allow_external_write = false
allow_dangerous = false
require_confirmation = trueMind classifies tools by permission level. The current permission levels are:
read_only
external_read
local_write
external_write
dangerous
Current default policy:
read_only allowed
external_read allowed
local_write allowed, with confirmation for confirmed tools
external_write disabled
dangerous disabled
This matters because the model is not trusted to execute arbitrary actions. The model may request a tool, but Mind checks the tool registry and configured permission policy before running it.
The [tools] config section controls which classes of tools are allowed:
[tools]
allow_external_read = true
allow_local_write = true
allow_external_write = false
allow_dangerous = false
require_confirmation = trueFor example, internet.github_zen is an external_read tool. If allow_external_read = false, Mind blocks that tool and returns a failed ToolResult instead of making the external request.
world.omens is also an external_read tool. It reads fixed public Earth and space monitoring APIs and accepts an optional max_items integer.
workspace.write_file, workspace.append_file, and project.devlog are local_write tools. In the default config they are allowed, but they still require confirmation when require_confirmation = true.
Show the default home output:
mindCheck basic environment status:
mind doctorPrint Mind's config and runtime state without calling the model:
mind inspectAsk a one-shot question:
mind ask "What should I work on next?"Ask using the configured uncensored model:
mind ask --uncensored "What should I work on next?"Ask using one or more files from the workspace as context:
mind ask "Summarize this file" --files notes.txtList files inside the controlled workspace:
mind filesStart an interactive chat session:
mind chatIn normal chat, Mind starts with a base system message and refreshes saved-memory context before each user turn. That means semantic memory retrieval uses the latest message as the query instead of injecting unrelated recent memories at session startup.
Start chat using the configured uncensored model:
mind chat --uncensoredStart chat with tools enabled:
mind chat --toolsTool-enabled chat sends each turn through the bounded agent loop and preserves prior turns as agent context.
Start chat with tools and trace output enabled:
mind chat --tools --traceRun the tool-using agent:
mind agent "What files are in my workspace?"Run the tool-using agent with trace output:
mind agent --trace "What files are in my workspace?"Run tool-enabled one-shot ask mode:
mind ask --tools "What files are in my workspace?"Store a manual memory:
mind remember "The project is named Mind."List saved memories:
mind memoriesList memories by review status:
mind memories --status auto_extractedDelete a memory by ID:
mind forget 1Review, backfill, or remove memory data:
mind memory confirm 1
mind memory reject 2
mind memory archive 3
mind memory delete 3
mind memory backfillList available tools:
mind toolsList saved agent runs:
mind runsShow a saved agent run:
mind run show 20260603-120000-deadbeefMind can run an explicitly configured alternate model:
mind uncensored "prompt"Mind reads, writes, and appends files only inside the configured workspace directory:
workspace/
File paths passed to --files and workspace tools are interpreted as workspace-relative paths.
For example:
mind ask "Summarize this" --files project-notes.mdreads:
workspace/project-notes.md
Mind rejects paths that attempt to escape the workspace, including absolute paths, parent-directory traversal, and symlink escapes.
Workspace write tools are conservative:
workspace.write_file
workspace.append_file
Safety behavior:
- write/append tools require confirmation when confirmation is enabled
- write refuses to overwrite existing files unless overwrite=true
- append can create missing files only when create=true
- content size is capped
- parent directories are created only after the target path passes workspace-boundary checks
Mind has read-only codebase tools for inspecting the configured project root:
codebase.list_files
codebase.read_file
The project root is configured with:
[project]
root = "."Codebase tools ignore runtime/build/cache paths such as:
.git
.venv
__pycache__
.pytest_cache
data
workspace
build
dist
*.egg-info
These tools are intended to let Mind inspect its own repo without arbitrary shell access.
Mind supports persistent memory through SQLite.
The database path is configured in:
[paths]
database = "data/mind.db"Manual memory commands:
mind remember "User prefers concise explanations."
mind memories
mind memories --status auto_extracted
mind forget 1
mind memory confirm 1
mind memory reject 2
mind memory archive 3
mind memory delete 3Memories are stored with normalized text for deduplication plus metadata for kind, source, review status, confidence, timestamps, and use counts. Storage helpers can resolve a normalized memory back to its database ID so derived data such as embeddings can be attached after insertion. Valid statuses are confirmed, auto_extracted, rejected, and archived. mind memories shows all stored memory records with metadata, while prompt injection and semantic retrieval use only active memories with confirmed or auto_extracted status.
The database also has a memory_embeddings table keyed by memory id and embedding model so semantic retrieval can store vectors without duplicating memory rows. The single-memory indexing helper resolves an existing memory ID, generates its embedding, and stores the vector using the configured model. When embeddings are enabled, mind remember indexes newly saved manual memories immediately. Embedding failures do not discard the saved memory; Mind prints a warning and mind memory backfill remains the recovery path. Embedding helpers can also store or replace one vector per memory/model pair, list active memories with vectors, and list active memories still missing vectors for a specific model. Retrieval embeds the query with the configured embedding model, ranks stored memory vectors by cosine similarity, and returns the highest-ranked memory IDs and text. Manual memories are saved as source = "manual", status = "confirmed", and confidence = 1.0.
During chat, Mind can also attempt experimental automatic memory extraction. After each assistant response, Mind asks the local model to extract durable facts from the conversation turn. Extracted memories are stored as source = "chat_auto", status = "auto_extracted", and confidence = 0.6. When embeddings are enabled, newly stored extracted memories are indexed immediately; duplicate memories are skipped, and indexing failures do not interrupt chat. Extracted memories can then be injected into future prompts when inject_context is enabled. Normal chat starts with a base system message, then refreshes saved-memory context before each user turn using that turn's text as the retrieval query. Query-specific prompts prefer semantic retrieval when embeddings are enabled, and fall back to recent memories if retrieval is unavailable.
Automatic memory extraction and prompt injection are controlled separately by:
[memory]
auto_extract = true
inject_context = true
max_relevant_memories = 8
min_similarity = 0.3min_similarity prevents semantic retrieval from injecting memories whose cosine
similarity score is below the configured threshold. If retrieval itself fails,
Mind falls back to recent memories.
Semantic embedding generation is configured separately:
[embeddings]
provider = "ollama"
model = "nomic-embed-text"
enabled = trueThe embedding helper currently supports Ollama and returns one numeric vector for each input text. It validates empty input, disabled embeddings, unsupported providers, provider failures, and malformed embedding responses before semantic retrieval consumes the vector.
Mind includes a bounded agent loop. Tool calls, total model calls, and invalid protocol repairs have separate limits so retries and future reasoning stages cannot create an unbounded run. The current defaults allow 10 tool calls, 20 total model calls, and 3 protocol repair retries.
The agent asks the local model to return one of two JSON response types.
Tool call:
{"type": "tool_call", "tool": "<available_tool_name>", "args": {"<arg_name>": "<arg_value>"}}Final answer:
{"type": "final", "answer": "<final-answer-text>"}When the model requests a tool, Mind:
- Parses the model output as JSON.
- Validates the response as either a
ToolCallorFinalAnswer. - Looks up the requested tool in the registry.
- Checks the tool's permission level against config.
- Runs confirmation if the tool requires it and confirmation is enabled.
- Runs only the approved Python function.
- Wraps the result in a structured
ToolResult. - Feeds the result back to the model.
- Asks for either another tool call or a final answer.
The current tool registry includes:
workspace.list_files
workspace.read_file
workspace.write_file
workspace.append_file
memory.list
codebase.list_files
codebase.read_file
internet.github_zen
world.omens
project.status
project.devlog
git.status
project.status returns a read-only project summary, including version, configured model, workspace/database/project paths, workspace file count, memory count, tool counts, and configured tool safety flags.
project.devlog appends a dated Markdown entry to workspace/devlog.md. It takes a required summary string and an optional next_steps list of strings, uses the same controlled workspace append boundary as workspace.append_file, and requires local-write permission plus confirmation.
git.status returns read-only git status --short --branch output for the configured project root. It accepts no arguments, does not expose arbitrary command execution, and truncates very large status output to 20,000 characters.
The important design rule is that the model does not directly execute arbitrary code. It may request a tool, but Python decides whether that tool exists, whether it is permitted, whether it needs confirmation, and how it runs.
Tool-enabled one-shot prompts are saved under the data directory:
data/runs/<run-id>/
Each saved run contains:
metadata.json
prompt.txt
final.md
trace.md
Use mind runs to list saved runs and mind run show <run-id> to inspect the prompt, final answer, trace output, and metadata. When trace mode is disabled, trace.md records that no trace was enabled for the run.
Confirmed tools do not call input() from the tool registry. Instead, interactive runtimes pass a confirmation callback into the agent/tool runner.
CLI/runtime confirmation callback
↓
run_agent(..., confirm=confirm_tool_run)
↓
run_tool(..., confirm=confirm_tool_run)
If a confirmed tool is called without a confirmation handler, Mind fails closed and returns a failed ToolResult.
Trace mode shows intermediate agent behavior.
Example:
mind agent --trace "What files are in my workspace?"Trace output may include:
Agent trace:
Step 1
Action: tool_call
Tool: workspace.list_files
Args: {}
Success: yes
Result preview:
Workspace files:
- notes.txt
Step 2
Action: final
Answer: Your workspace contains notes.txt.
Trace mode is useful for debugging tool selection, permission failures, protocol failures, confirmations, and agent loops. Long tool outputs and raw invalid model responses are shown as previews so terminal traces stay readable.
Mind is currently organized around a package-based architecture:
CLI
↓
Runtime modes
- ask
- chat
- agent
↓
Core infrastructure
- config
- context
- prompts
- diagnostics
- embeddings
- LLM client
↓
Agent loop
↓
Tool registry
↓
Tools
- workspace
- codebase
- memory
- internet
↓
Workspace / Codebase / SQLite / External APIs
Current package responsibilities:
mind/cli/ CLI parser and command handlers
mind/runtime/ ask/chat runtime flows and interactive confirmation
mind/core/ config, context, prompts, diagnostics, embeddings, and LLM client
mind/agent/ bounded agent loop, protocol parsing, trace output, and agent prompts
mind/tools/ tool registry, tool specs, tool results, permission checks, and tool implementations
mind/memory/ SQLite memory store and memory extraction
mind/workspace/ safe workspace file access
mind/codebase/ safe read-only codebase file access
Mind's current safety boundaries:
- workspace-relative file access
- project-root-relative codebase access
- rejection of path traversal outside workspace/project root
- rejection of symlink escapes
- ignored runtime/build/cache paths for codebase tools
- confirmation-required local write tools
- overwrite protection for workspace writes
- content size caps for workspace writes/appends
- bounded agent loop
- explicit tool registry
- structured agent protocol validation
- structured tool results
- configurable tool permission enforcement
- no arbitrary shell execution
- no external write tools enabled
Mind is intentionally conservative. New capabilities should be added as small controlled tools, not as unrestricted model behavior.
Run the test suite:
pytestRun targeted tests:
pytest tests/test_workspace.py
pytest tests/test_codebase.py
pytest tests/test_tools.py
pytest tests/test_agent.pyThe tests currently cover:
- CLI command routing
- Context building
- Ask runtime behavior
- Chat loop behavior
- Manual memory storage
- Memory metadata storage
- Memory deduplication
- Memory deletion
- Memory review status updates
- Memory embedding backfill
- Agent run persistence and inspection commands
- Memory formatting
- Memory embedding storage and lookup
- Memory semantic retrieval ranking
- Automatic memory extraction parsing
- Embedding generation and provider response validation
- Workspace file listing
- Safe workspace file reading, writing, and appending
- Codebase file listing and reading
- Prompt construction
- Config loading
- Agent JSON parsing
- Agent protocol retry behavior
- Agent tool loop behavior
- Agent trace output
- Tool registry behavior
- Read-only Git status tool behavior
- Structured tool results
- Tool permission enforcement
- Tool confirmation behavior
- Workspace, codebase, memory, and internet tools
Planned development stages:
- Basic CLI and local model interaction
- Safe workspace file access
- Centralized context construction
- Persistent memory
- Split automatic extraction from prompt context injection.
- Wire semantic memory retrieval into context building with recent-memory fallback.
- Bounded agent loop
- Tool registry and controlled tool execution
- Tool metadata and permission enforcement
- Agent trace/debug mode
- Safe local write and append tools
- Read-only codebase tools
- Project/devlog/status tools
- Memory review workflow and retrieval improvements
- Mission/run history
- Controlled test-runner tool with explicit local-execute permission
- Optional retrieval-augmented generation over local files and memories
- Optional integrations for GitHub, email drafts, calendar, web search, project workflows, and automation
Near-term next steps:
1. Wire semantic memory retrieval into context building.
2. Add mission/run history.
3. Add a controlled test-runner tool with explicit local-execute permission.
4. Add model provider abstraction.
Mind should be:
- Local-first
- Understandable
- Extensible
- Safe by default
- Useful from the command line
- Built around clear internal layers
- Easy to extend with small Python tools
- Explicit about what the model can and cannot do
- Capable of growing into a more powerful personal assistant without becoming a messy wrapper script