MemorySmith
MemorySmith is a single-host ASP.NET Core app for local structured memory management. It hosts a Blazor workbench UI, markdown pages, REST API, MCP endpoint, file-backed content storage, SQLite-backed security/audit metadata, local chat/agent workflows, and background maintenance in one process. The /memories page is the primary structured memory workbench. The repository ships with a live project wiki inside Data/Memories, and the app uses its own memory store as a testbed.
Quick Start
dotnet run --project MemorySmith.App --launch-profile http
Opens on http://localhost:5089 by default. Pages:
| Route | Purpose |
|---|---|
/memories |
Browse, search, create, edit, delete memory records |
/pages |
Create, search, edit, preview, and render markdown-backed pages from Data/Pages |
/chat |
Memory-enhanced chat and agent mode with provider/model selection, streaming responses, context usage, attachments, and local chat history |
/login, /admin/setup, /admin |
Local sign-in, first-admin bootstrap, and RBAC/audit/history administration |
/health |
Scrollable stat cards, activity charts (queries/day, changes/day), maintenance telemetry |
/variables |
Manage %VarName% path variables used in source link URIs |
/about |
MemorySmith and third-party license information |
/api/memories |
REST CRUD for automation |
/api/pages, /api/search, /api/chat |
Page CRUD/search/rendering, combined memory/page search, and chat/agent/config API |
/api/auth/*, /api/admin/* |
Current-user, login/logout, setup, user, provider, audit, and history metadata APIs |
/api/stats, /api/health/*, /api/diagnostics |
Stats, readiness, and redacted operational diagnostics |
/page-assets/* |
Static files from Data/Pages/assets for images, video, and audio embedded in pages |
/mcp |
MCP JSON-RPC endpoint for AI agent tool use |
The Project Wiki
Data/Memories/ is the structured live wiki for this project. Data/Pages/ is the markdown live wiki for longer-form user and agent-authored notes. The app defaults MemorySmith:DataPath to ../Data/Memories and MemorySmith:PagesPath to ../Data/Pages, so local runs read and write those records directly.
User-created markdown files under Data/Pages/ are valid project wiki content and should be committed unless a specific page is intentionally private or temporary.
Using the wiki as a testbed: before starting research or making architectural decisions, search the wiki first. The wiki records are also used as integration test fixtures — tests copy Data/Memories/ to a temp directory so the source stays stable while being exercised through the same code paths.
Current wiki records
| ID | What it documents |
|---|---|
project-wiki-active-architecture |
Single-host layout, project structure |
project-wiki-data-folder-policy |
Data/ folder conventions and test fixture policy |
project-wiki-storage-rules |
FileMemoryStore behavior, atomic writes, ID sanitization |
project-wiki-event-store |
FileEventStore JSONL audit log, activity buckets |
project-wiki-source-links-feature |
SourceLink model, %VarName% expansion, source_bundle/find_by_source tools |
project-wiki-ui-architecture |
Blazor Server pages, MudBlazor 9.4, CSS conventions |
project-wiki-semantic-ui-current |
/memories workbench feature set |
project-wiki-mcp-integration |
MCP endpoint setup, VS Code mcp.json config |
project-wiki-mcp-search-tools-current |
All seven MCP tool signatures and usage notes |
project-wiki-mcp-context-pack |
memorysmith_context_pack deep-dive |
project-wiki-onnx-semantic-embeddings |
Optional ONNX Runtime embedding ranker and exact cosine semantic fallback path |
project-wiki-hybrid-search-rrf |
Lucene.NET + semantic RRF fusion |
project-wiki-semantic-search-gap |
Remaining semantic-search limitations and future vector-index gap |
project-wiki-search-roadmap |
Planned search improvements |
project-wiki-validation-command |
How to build and test |
project-wiki-test-architecture |
NUnit fixture strategy, benchmark suite |
project-wiki-windows-service-operations |
Windows Service install/uninstall flags |
project-wiki-markdown-pages |
Markdown page storage, rendering, and page assets |
project-wiki-chat-agent-provider |
Chat provider/agent abstractions, Ollama streaming, and GitHub Copilot provider workflow |
project-wiki-chat-image-attachments |
Image attachment pipeline, trusted temp storage, and vision payload routing |
project-wiki-chat-local-storage-persistence |
Browser-local chat history, draft retention, and provider/model selection persistence |
project-wiki-chat-streaming-thinking |
Streaming response chunks, thinking-block extraction, and elapsed timers |
project-wiki-agent-instructions-source-of-truth |
Copilot instruction files and current agent-facing source-of-truth map |
project-wiki-ui-layout-source-link-polish |
UI layout, source-link open behavior, and navigation polish |
project-wiki-scope-boundaries |
What is and isn't in scope for the current implementation |
project-wiki-generalization-friction |
Known gaps for broader adoption |
project-wiki-benchmarkdotnet-suite |
BenchmarkDotNet project: smoke validation and full benchmark commands |
project-wiki-semantic-tool-quality-suite |
Search relevance probes, aggregate MRR, and MCP tool output quality assertions |
project-wiki-current-validation-146-tests |
Historical validation baseline: 146 NUnit tests across the solution |
project-wiki-github-actions-artifacts |
CI Cobertura coverage artifacts and Doxygen GitHub Pages export |
project-wiki-maintenance-observability-refinements |
Startup triage/index scheduling and stats activity bucket API |
project-wiki-operational-diagnostics-dashboard |
/health dashboard and /api/diagnostics operational snapshot |
project-wiki-request-guard-hardening |
Request guard middleware, AllowRemoteApi and ApiKey enforcement |
project-wiki-admin-auth-hardening |
Admin-policy hardening and editable settings current state |
project-wiki-source-link-security-boundaries |
Source bundle read boundaries and allowed root variable rules |
project-wiki-test-fixture-overview |
Overview of the five integration-test fixture records |
project-wiki-test-fixture-context-root |
Context pack root fixture (context pack traversal tests) |
project-wiki-test-fixture-reference-child |
Reference child fixture |
project-wiki-test-fixture-backlink-source |
Backlink source fixture |
project-wiki-test-fixture-conflict-note |
Conflict fixture |
Retrieve any record via the MCP tool memorysmith_get with its ID, or search the /memories page.
Authentication, Audit, And History
MemorySmith keeps memory/page content file-backed and stores security metadata in SQLite at Data/memorysmith.db by default. Cookie authentication and RBAC policies protect the UI, REST APIs, and MCP tools. Built-in roles are Viewer, Editor, and Admin; the default local policy allows anonymous Viewer access, while mutation, diagnostics, admin, audit, settings, and restore workflows require stronger roles.
On a fresh local install, Auth:OpenLocalEditorCompatibility grants loopback requests non-admin local write compatibility until the first Admin user exists. Admin, user-management, settings, audit, diagnostics, and restore workflows always require a signed-in Admin user. Create that first account at /admin/setup, then sign in at /login. Local password auth is implemented; external provider rows for GitHub, Google, and Microsoft are seeded for administration and later OAuth wiring.
Audit metadata is written to SQLite and mirrored to weekly JSONL files under Data/Events. Memory and page writes also create version-history artifacts under Data/.history; these artifacts are metadata/history records, not replacements for Data/Memories or Data/Pages as the source of truth.
Memory Records
Each record is a JSON file in Data/Memories/{Status}/. Fields:
| Field | Type | Description |
|---|---|---|
Id |
string | Unique, kebab-case. Must match the filename. |
Title |
string | Short human name. |
Content |
string | Body text. Max 20 000 chars. Rendered as safe Markdown in the /memories detail pane. |
Status |
int | 0 Unconsolidated · 1 Working · 2 Core · 3 Deprecated |
Confidence |
double | 0.0–1.0 |
Tags |
string[] | Comma-separated labels for filtering. |
References |
string[] | IDs of related records (used for context pack traversal). |
Conflicts |
string[] | IDs of conflicting records. |
SourceLinks |
array | File references — see below. |
UsageCount |
int | Incremented by explicit usage API/UI calls. Read-only MCP tools do not mutate it. |
LastUpdated |
datetime | ISO 8601 UTC. |
Source Links
Each SourceLink in the array has:
{ "Label": "Program.cs", "Uri": "%MemorySmithRepo%MemorySmith.App/Program.cs", "StartLine": 1, "EndLine": 50 }
Urimay contain%VarName%tokens. Manage variable bindings at/variables; they are stored inData/vars.json.StartLine/EndLineare optional 1-based line numbers. When onlyStartLineis set the default window is 50 lines.- The MCP tool
memorysmith_source_bundleresolves URIs and returns actual file content slices alongside the memory data. - The MCP tool
memorysmith_find_by_sourcescans all records for source links matching a URI substring pattern.
Add or edit source links in the /memories workbench using the format Label | URI [| StartLine[-EndLine]], one per line.
Local file source-link chips copy the resolved path on click. Ctrl+Click opens the resolved file with the operating system default app when SourceLinks:AllowOpenWithDefaultApp is enabled and the path is under an allowed source root.
Markdown Pages
Data/Pages/ stores user and agent-editable markdown files. The /pages UI and /api/pages API keep page search and page navigation separate from structured memory search. /api/search returns a combined memory/page result set when broader discovery is useful. Page assets live under Data/Pages/assets and are served at /page-assets; markdown links such as  are rewritten to that static route when rendered.
The page editor has a markdown toolbar for common inserts, an image upload/embed tool that writes to Data/Pages/assets, a toggleable live preview, a manual preview refresh button, and an unsaved-change prompt for internal and external navigation. Pages are rendered with the shared Markdig pipeline, including Mermaid fenced blocks (```mermaid) and Prism-compatible fenced code classes such as language-csharp or language-json. Raw HTML is disabled by default for rendered pages; trusted local deployments can enable MemorySmith:Pages:AllowRawHtml when raw HTML media tags are intentionally needed. The static docs-site generator also sanitizes rendered page HTML and emits a restrictive Content Security Policy before publishing wiki pages.
Chat and Agent Mode
/chat uses the IChatProvider and IChatAgent abstractions. The registered providers are OllamaChatProvider, which calls a local Ollama HTTP service, and GitHubCopilotChatProvider, which uses the GitHub Copilot SDK with GitHub CLI authentication or a configured token environment variable. MemoryChatAgent now uses intent-aware preloading: exact-reply/simple prompts and write-only Agent commands skip local wiki pre-context, while explicit MemorySmith/wiki/codebase prompts receive a small bounded hybrid memory plus page preload. When preloaded context is absent or not enough, the shared prompt lets the model request an app-intercepted, MCP-compatible read-only wiki tool call by returning JSON such as {"toolCalls":[{"name":"memorysmith_unified_search","arguments":{"query":"search text","memoryLimit":5,"pageLimit":5}}]}. The chat allowlist now matches the read-only MCP surface: memorysmith_search, memorysmith_semantic_search, memorysmith_hybrid_search, memorysmith_context_pack, memorysmith_get, memorysmith_page_search, memorysmith_page_get, and memorysmith_unified_search. A deterministic intent interceptor (ChatIntentInterceptor) also pre-runs an obvious tool call when the user message starts with phrases like "search the wiki for ...", "open page ", "get memory ", "semantic/hybrid search ...", or "context pack ...", so reliable retrieval does not depend on the model emitting the JSON tool-call protocol correctly. Retrieved tool output and preloaded context are wrapped in an "Untrusted retrieved data" preamble before being added to the model context so any embedded instructions are treated as data, not commands.
The chat UI queries the selected provider for available models, supports provider/model selection, persists the last used provider/model, Mermaid diagram theme mode, and active chat, keeps the top model bar and bottom composer stable when switching the shared sidebar between History and Trace, places the sidebar toggle at the right edge beside the shared sidebar, streams live response chunks with an elapsed timer, renders message bodies as safe Markdown through Markdig with raw HTML disabled, supports Mermaid diagrams and Prism-compatible fenced code highlighting, lets users choose Auto/Light/Dark Mermaid theme mode, wraps rendered diagrams in a matching readable light or dark surface, defers Mermaid conversion while a response is actively streaming so unfinished fences remain visible as code, shows the provider/model used on assistant turns, shows per-response durations, displays bottom-right context usage with context-window percentage when known and provider quota/rate text when reported, deletes chats from history with confirmation, supports icon Stop for immediate cancellation plus icon Finish Step for a softer stop after the current provider/tool step, supports text and image file attachments, supports pasted clipboard images, Enter-to-send with Shift+Enter newlines, autoscroll, clickable memory/page resource chips behind a collapsed per-turn References drawer, pending-response feedback, compact browser-local chat history, and collapsible thinking blocks when the provider returns reasoning content. History and Trace share one right sidebar with tabs; Trace shows the selected turn's execution graph, turn selector, reasoning, tool requests/results, write approvals, token estimates, and tool latency with filters, collapsible trace headers, and editable tool rerun. The transcript no longer renders per-turn Trace buttons. Neutral resource chips are preloaded context, blue resource chips are mid-turn tool/intercept resources, and green write chips are Agent-created pages. Text attachments are bounded and supplied as context. Image attachments are saved to trusted temp files for persistence and supplied as native image payloads when the selected provider supports them; Ctrl+V handles copied image files, Clipboard API image blobs, copied HTML image references, and data:image URLs. Draft text and queued attachments are retained per chat session when switching chats, and navigation warns before leaving with unsent content.
Chat mode answers questions and the shared prompt asks providers to format normal answers as GitHub-flavored Markdown. The prompt also gives all chat agents explicit guidance for when to use preloaded wiki context, when to request a single app-intercepted read-only toolCalls JSON object, and how to produce complete Mermaid fenced diagrams only when a diagram clarifies the answer. Agent mode asks the provider for structured actions and can write memories and pages only when Chat:AgentWritesEnabled is explicitly set to true; the default is false. The chat UI still requires explicit per-action approval before applying proposed Agent writes. Tool-call execution is read-only and bounded by Chat:MaxToolIterations, Chat:MaxToolCallsPerTurn, and Chat:MaxToolResultCharacters; write actions still require Agent mode structured output and the opt-in write setting. The shared system prompt is stored in MemorySmith.Core/Docs/Prompts/wiki-chat-agent.md and copied into the app output for service/publish runs; MemorySmith.Core/Docs/Prompts/wiki-chat-agent.modelfile carries the matching Athena/Ollama system prompt.
The provider interface is intentionally narrow so OpenAI, Anthropic, or other APIs can be added without changing the UI or agent workflow.
Search
Three search modes are available in the UI (/memories search bar) and the REST API:
| Mode | Endpoint | Behavior |
|---|---|---|
| Lexical | POST /api/memories/search |
Lucene.NET StandardAnalyzer tokenization and weighted title/tag/reference/content scoring. Empty queries retain deterministic LastUpdated ordering. |
| Semantic | POST /api/memories/search/semantic |
ONNX embedding cosine ranking when SemanticSearch model and vocabulary files are available; otherwise local token/tag/title/reference/alias scoring with match explanations. |
| Hybrid | POST /api/memories/search/hybrid |
Lucene.NET lexical analysis + the active semantic ranker, fused with Reciprocal Rank Fusion (RRF). Best for discovery. |
All three accept query, tags (comma-separated, filter by any match), status, and limit.
The embedding path uses ONNX Runtime, a local WordPiece vocabulary, E5-style query:/passage: prefixes, and an exact in-memory cosine scan over the filtered memory set. It intentionally falls back to the existing token scorer when model assets are missing or unusable, so fresh clones still work without redistributing model binaries.
MCP Tools
The MCP endpoint is at http://localhost:5089/mcp. VS Code config lives in .vscode/mcp.json. Twelve tools are exposed (eight read-only chat-allowlisted, two write tools requiring edit permission, plus two source-aware tools available only over MCP):
| Tool | Key args | Returns | Permission |
|---|---|---|---|
memorysmith_search |
query, tags, status, limit |
Lexical results | View |
memorysmith_semantic_search |
query, tags, status, limit |
Scored results with match reasons | View |
memorysmith_hybrid_search |
query, tags, status, limit |
RRF-ranked results | View |
memorysmith_context_pack |
query or ids, tags, referenceDepth, includeBacklinks, maxRecords, maxContentChars, format |
Search results + linked references/conflicts in one response | View |
memorysmith_get |
id |
Single read-only record by ID | View |
memorysmith_page_search |
query, limit |
Markdown page summaries from Data/Pages |
View |
memorysmith_page_get |
slug, maxCharacters |
One markdown page body, bounded for safe context inclusion | View |
memorysmith_unified_search |
query, memoryLimit, pageLimit, tags, status |
One call across memories + pages, returning separate memory and page result sections | View |
memorysmith_page_save |
markdown, slug (opt), title (opt) |
Creates or updates a wiki page; returns slug, title, and updated timestamp | Edit |
memorysmith_page_delete |
slug |
Deletes a wiki page; returns success or not-found | Edit |
memorysmith_source_bundle |
ids or query/tags/limit, maxFileBytes, format |
Records + resolved file content slices for every source link (MCP only) | Source bundle |
memorysmith_find_by_source |
pattern |
Records whose source link URIs match the substring (MCP only) | View |
memorysmith_context_pack tips:
- Use query for open-ended discovery; use ids for anchoring to known records.
- referenceDepth=1 follows one hop of References and Conflicts from root records.
- includeBacklinks=true adds records that reference the roots.
- format=json returns structured JSON for agent parsing; the default is Markdown prose.
- The tool reports warnings for missing roots, missing links, or records omitted after hitting maxRecords.
memorysmith_source_bundle tips:
- Combine with memorysmith_context_pack results: get the context pack first, then bundle the source for the most relevant records.
- format=jsonl returns one JSON object per line (streaming-friendly); format=json returns a single object with memoryCount, sourceCount, and entries.
Configuration
All settings live under MemorySmith in appsettings.json:
{
"MemorySmith": {
"DataPath": "../Data/Memories",
"PagesPath": "../Data/Pages",
"EventLogPath": "../Data/Events/audit.log",
"VarsPath": "../Data/vars.json",
"ApiKey": null,
"AllowRemoteApi": false,
"DataProtectionKeysPath": "../Data/Keys",
"SettingsOverridePath": null,
"Database": {
"Provider": "SQLite",
"ConnectionString": "Data Source=../Data/memorysmith.db",
"ApplyMigrationsOnStartup": true,
"UseWal": true,
"BusyTimeoutSeconds": 30
},
"Auth": {
"Enabled": true,
"AnonymousAccess": "Viewer",
"AuthenticatedDefaultRole": "Viewer",
"AutoEditorForAuthenticatedUsers": false,
"LocalPasswordEnabled": true,
"OpenLocalEditorCompatibility": true
},
"Audit": {
"JsonlEnabled": true,
"JsonlPath": "../Data/Events/audit-{yyyy}-W{week}.jsonl"
},
"History": {
"RootPath": "../Data/.history",
"PageMode": "Snapshot",
"MemoryMode": "JsonPatchWithCheckpoints"
},
"Pages": {
"AllowRawHtml": false
},
"SemanticSearch": {
"EmbeddingsEnabled": true,
"ModelPath": "Models/embedding-model.onnx",
"VocabularyPath": "Models/vocab.txt",
"MaxInputTokens": 512,
"MaxIndexedTextCharacters": 6000,
"QueryPrefix": "query: ",
"DocumentPrefix": "passage: "
},
"Maintenance": {
"Enabled": true,
"TriageMinutes": 5,
"IndexingMinutes": 60,
"ConsolidationHours": 24,
"StartupGraceSeconds": 30
},
"SourceLinks": {
"MaxReadBytes": 65536,
"AllowOpenWithDefaultApp": true,
"AllowedFileRootVariables": [ "MemorySmithRepo" ],
"AllowedFileRoots": []
},
"Chat": {
"Provider": "Ollama",
"OllamaEndpoint": "http://localhost:11434",
"OllamaModel": "gemma4:e4b",
"OllamaContextWindowTokens": null,
"GitHubModel": "gpt-4.1",
"GitHubTokenEnvironmentVariable": "GITHUB_TOKEN",
"GitHubModels": [
{ "Name": "gpt-4.1", "ChatMultiplier": 0, "IsPreferred": true, "Description": "Free/standard Copilot GPT option when available" },
{ "Name": "gpt-4.1-mini", "ChatMultiplier": 0, "IsPreferred": true, "Description": "Free/low-cost GPT mini option when available" },
{ "Name": "gpt-4o-mini", "ChatMultiplier": 0, "IsPreferred": true, "Description": "Free/low-cost GPT-4o mini option when available" },
{ "Name": "claude-3.5-haiku", "IsPreferred": true, "Description": "Lower-cost Claude Haiku option before Sonnet" },
{ "Name": "gpt-5.1-mini", "Description": "GPT-5.1 mini option when available" },
{ "Name": "gpt-4o", "Description": "GPT-4o option when available" },
{ "Name": "gpt-5", "Description": "GPT-5 option when available" },
{ "Name": "claude-sonnet-4.5", "Description": "Claude Sonnet option when available after cheaper candidates" }
],
"SystemPromptPath": "Prompts/wiki-chat-agent.md",
"RequestTimeoutSeconds": 600,
"MaxContextRecords": 5,
"MaxContextPages": 5,
"MaxContextItemCharacters": 4000,
"MaxHistoryMessages": 16,
"MaxAttachmentCharacters": 120000,
"MaxAttachmentBytes": 8388608,
"ToolCallsEnabled": true,
"MaxToolIterations": 2,
"MaxToolCallsPerTurn": 3,
"MaxToolResultCharacters": 12000,
"AgentWritesEnabled": false
}
}
}
Override via appsettings.Development.json or environment variables (MemorySmith__DataPath, etc.).
ApiKey— if set, all API and MCP requests must includeX-Api-Key: <value>. Leavenullfor local use. The shared API key can satisfy non-admin API/MCP policies; it does not grant admin, user-management, settings, audit, diagnostics, or restore access.AllowRemoteApi— settrueto allow non-localhost callers. Off by default.DataProtectionKeysPath— stores ASP.NET Core cookie/data-protection keys outside build output so local sign-in cookies survive app restarts.Database:*— controls the SQLite metadata database used for users, roles, provider links, login history, audit metadata, version metadata, token metadata, admin settings, and semantic-index metadata. Content files remain inData/MemoriesandData/Pages.SettingsOverridePath— optional path for admin-edited local settings. Defaults toappsettings.LocalDevelopment.jsonbeside the running app.Auth:*— controls cookie/RBAC behavior.AnonymousAccess=Viewerkeeps local browsing open by default; config-derived anonymous/default roles are clamped below Admin, andOpenLocalEditorCompatibility=truepreserves pre-setup loopback write compatibility only for non-admin operations.Audit:*— controls the weekly JSONL audit mirror. SQLite remains the queryable metadata store.History:*— controls version-history artifact storage for memory and page mutations.Pages:AllowRawHtml— enables trusted raw HTML rendering in markdown pages. Off by default; leave disabled for agent-written or unreviewed pages.SemanticSearch:*— controls optional ONNX embedding ranking. Relative model and vocabulary paths resolve from the configured data deployment root: the folder that containsMemories,Events,Graph,Models, andPages. The default model path isModels/embedding-model.onnx; ONNX/model artifacts are ignored by Git, and a matching WordPiecevocab.txtis required before embeddings activate. Legacy../Data/Models/...values are also interpreted relative to that data root.DataPath— root of the memory store. Subdirectories (Unconsolidated/,Working/,Core/,Deprecated/) are created automatically.PagesPath— root of the markdown page store.assets/under this directory is served at/page-assets.VarsPath— path to the flat JSON dict used for%VarName%source link expansion.SourceLinks:MaxReadBytes— maximum local file content returned per source-link entry by MCP source bundle reads.SourceLinks:AllowOpenWithDefaultApp— allows Ctrl+Click source-link opening after variable resolution and allowed-root checks.SourceLinks:AllowedFileRootVariables— variable names whose resolved values are trusted roots for local source-link file reads. Defaults toMemorySmithRepo.SourceLinks:AllowedFileRoots— optional explicit local roots, useful when source links need access outside the repo wiki root.Chat:*— provider, Ollama endpoint/model, GitHub Copilot model/token environment settings, prompt path, timeout, context/history/attachment limits, read-only intercepted wiki tool-call limits, and whether agent-mode writes are enabled.AgentWritesEnabledis false by default; enabling it permits Agent mode structured provider output to write memories and pages directly.PreloadContextEnabled,MaxPreloadedContextRecords, andMaxPreloadedContextPagescontrol the small automatic pre-context used only for explicit local-knowledge prompts.MaxContextItemCharactersbounds each memory/page item sent to the chat provider,MaxAttachmentCharactersbounds text attachments, andMaxAttachmentBytesbounds uploaded/pasted files.ToolCallsEnabledallows models to request app-executed MemorySmith search/context/get calls inside the same user turn, whileMaxToolIterations,MaxToolCallsPerTurn, andMaxToolResultCharactersbound cost and result size.OllamaContextWindowTokensis optional metadata for the UI usage meter when a local model's context window is known. SetOllamaModelto a model returned byollama list; setGitHubModelto a Copilot model available to the authenticated GitHub account. The configured GitHub fallback order prefers free/low-cost GPT models first, then Claude Haiku, then Sonnet. The UI can query providers directly for available models and stores the last selected provider/model in browser storage.
Windows Service
Publish the app, then from an elevated PowerShell session:
# Install
.\MemorySmith.App.exe install --service-name MemorySmith --service-display-name "MemorySmith" --memory-directory C:\MemorySmith\Memories --port 5089
# Uninstall
.\MemorySmith.App.exe uninstall --service-name MemorySmith
# Help
.\MemorySmith.App.exe --help
Install flags:
| Flag | Purpose |
|---|---|
install, --install-service |
Create the Windows Service |
uninstall, --uninstall-service |
Stop and delete the Windows Service |
--service-name |
Service name. Default: MemorySmith |
--service-display-name |
Display name in Services UI |
--service-description |
Windows Service description |
--service-start-type |
auto, demand, or disabled |
--memory-directory |
Target MemorySmith:DataPath; adjacent Pages, Events/audit.log, and vars.json are derived from its parent folder |
--port |
Local HTTP port. Default install port: 5089 |
Arguments after -- are still passed as runtime args to the service process for advanced ASP.NET Core settings. Use either --port or a custom runtime --urls, not both.
For this repository's live project wiki, the target memory directory is C:\Users\norrt\source\repos\MemorySmith\Data\Memories. A local service install on port 5089 would be:
.\MemorySmith.App.exe install --memory-directory C:\Users\norrt\source\repos\MemorySmith\Data\Memories --port 5089
After installation, start the service from services.msc or PowerShell, then open http://localhost:5089/health for runtime configuration, storage diagnostics, activity, and maintenance telemetry.
Validate
dotnet build MemorySmith.slnx -v minimal
dotnet test MemorySmith.slnx -v minimal
Collect local Cobertura coverage with the same collector used by CI:
dotnet test MemorySmith.slnx --configuration Release --collect:"XPlat Code Coverage" --results-directory artifacts/TestResults -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura
Generate the Doxygen wiki locally when Doxygen and Graphviz are installed:
doxygen docs/Doxyfile
Run BenchmarkDotNet search benchmarks:
dotnet run -c Release --project MemorySmith.Benchmarks -- --smoke
dotnet run -c Release --project MemorySmith.Benchmarks -- --filter *SearchBenchmarks*
The solution builds MemorySmith.App as the single deployable host. MemorySmith.Tests includes 179 NUnit tests: unit tests, integration tests (via WebApplicationFactory), SQLite metadata coverage, auth/audit/history coverage, Markdown rendering coverage, and a [Category("Benchmark")] suite of search quality probes with latency thresholds. GitHub Actions collects Cobertura coverage in CI and publishes a Doxygen HTML wiki through the Pages workflow.