agentleFS
ProfSynapseProfSynapse/nexus/AGENTS.md

nexus

AGENTS.md158 starsChanged 6 days agoMITView on GitHub

This is an Obsidian community plugin that must follow official Plugin Guidelines. All code changes must adhere to these best practices. Key lifecycle rules: - All registration methods (registerEvent, addCommand, registerView, registerInterval) auto-cleanup on unload - Never store view references in the plugin instance (causes memory leaks) - Use this.app.workspace.onLayoutReady() to defer startup operations CRITICAL: All styles must be defined in styles.css, never inline in TypeScript/JavaScript. Required CSS Variables (never hardcode colors): | Variable | Purpose | |----------|---------| | --text-normal,…

The file

We split this AGENTS.md into the rules it gives an agent

12 rules. About 0 are common, 3 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    All registration methods (registerEvent, addCommand, registerView, registerInterval) auto-cleanup on unload

    line 33
  2. Distinctive:

    Never store view references in the plugin instance (causes memory leaks)

    line 34
  3. Very specific:

    Use this.app.workspace.onLayoutReady() to defer startup operations

    line 35
  4. Very specific:

    CRITICAL: All styles must be defined in styles.css, never inline in TypeScript/JavaScript.

    line 39
  5. Very specific:

    Do not improvise the process from this note.

    line 266
  6. Very specific:

    Note for existing stale conversations: Use the Nexus: Rebuild cache command to force SQLite cache rebuild from the synced event store.

    line 317
  7. Very specific:

    No delete — the AI gets archive (reversible).

    line 425
  8. Very specific:

    No session tools — sessions are context fields, not tools.

    line 443
  9. Distinctive:

    If startup hangs on "loading cache", clear site data

    line 518
  10. Distinctive:

    v5.0.0 Deprecation Cleanup: Remove backward compatibility for old dedicated agent structures (TODO(v5.0.0) in WorkspacePromptResolver)

    line 525
  11. Very specific:

    Status: All major issues addressed (Dec 2025).

    line 531
  12. Very specific:

    Instead of 50+ tools, MCP exposes just 2: getTools (discovery) and useTools (execution).

    line 584

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

Codex Context Document

Last Updated: 2026-04-04

Project Overview

  • Name: Claudesidian MCP
  • Version: 5.11.1
  • Type: Obsidian Community Plugin
  • Purpose: MCP integration for Obsidian with AI-powered vault operations
  • Architecture: Agent-Tool pattern with domain-driven design
  • Stack: TypeScript, Node.js, Obsidian Plugin API, MCP SDK

Obsidian Plugin Development Guidelines

This is an Obsidian community plugin that must follow official Plugin Guidelines. All code changes must adhere to these best practices.

Plugin Lifecycle

// Plugins extend the Plugin base class
export default class MyPlugin extends Plugin {
    async onload() {
        // Initialize UI, register commands, set up events
        // Use registration methods for auto-cleanup
    }

    async onunload() {
        // Clean up resources (most handled automatically)
    }
}

Key lifecycle rules:

  • All registration methods (registerEvent, addCommand, registerView, registerInterval) auto-cleanup on unload
  • Never store view references in the plugin instance (causes memory leaks)
  • Use this.app.workspace.onLayoutReady() to defer startup operations

Styling - ALL STYLES IN styles.css

CRITICAL: All styles must be defined in styles.css, never inline in TypeScript/JavaScript.

// ❌ NEVER do this
element.style.color = 'white';
element.style.backgroundColor = 'red';
element.style.display = 'flex';

// ✅ ALWAYS do this
element.addClass('my-plugin-element');
/* In styles.css - use CSS variables for theme compatibility */
.my-plugin-element {
    color: var(--text-normal);
    background-color: var(--background-primary);
    display: flex;
}

Required CSS Variables (never hardcode colors):

VariablePurpose
--text-normal, --text-muted, --text-faintText colors
--text-accentInteractive/link text
--background-primary, --background-secondaryBackground colors
--background-modifier-borderBorders
--background-modifier-errorError states
--interactive-accent, --interactive-accent-hoverButtons/interactive
--radius-s, --radius-m, --radius-lBorder radius

Security Requirements

innerHTML is FORBIDDEN with dynamic content:

// ❌ NEVER - XSS vulnerability
element.innerHTML = userProvidedContent;
element.innerHTML = `<div>${dynamicData}</div>`;

// ✅ Safe patterns
element.textContent = userProvidedContent;  // For text
element.createEl('div', { text: dynamicData });  // Obsidian API

// ✅ Safe innerHTML patterns (only these are acceptable)
element.innerHTML = '';  // Clearing
const escaped = div.innerHTML;  // Reading already-escaped content

Safe DOM creation with Obsidian API:

// Use createEl, createDiv, createSpan
const container = contentEl.createDiv({ cls: 'my-container' });
const heading = container.createEl('h2', { text: 'Title' });
const button = container.createEl('button', {
    text: 'Click me',
    cls: 'my-button'
});

// For icons, use setIcon
import { setIcon } from 'obsidian';
setIcon(button, 'chevron-right');

Event Registration

Always use registerDomEvent for DOM events:

// ❌ NEVER - causes memory leaks on unload
element.addEventListener('click', handler);
document.addEventListener('keydown', handler);
window.addEventListener('resize', handler);

// ✅ ALWAYS - auto-cleanup on unload
this.registerDomEvent(element, 'click', handler);
this.registerDomEvent(document, 'keydown', handler);
this.registerDomEvent(window, 'resize', handler);

// ✅ For Obsidian workspace events
this.registerEvent(this.app.vault.on('modify', handler));
this.registerEvent(this.app.workspace.on('active-leaf-change', handler));

File Operations

// ❌ NEVER use vault.adapter directly (mobile incompatible)
await this.app.vault.adapter.read(path);
await this.app.vault.adapter.write(path, content);

// ✅ Use Vault API
await this.app.vault.read(file);
await this.app.vault.cachedRead(file);  // Faster, uses cache

// ✅ For modifying files, use Vault.process() (atomic, prevents conflicts)
await this.app.vault.process(file, (content) => {
    return content.replace('old', 'new');
});

// ✅ Use Editor API for active file (preserves cursor)
const editor = this.app.workspace.activeEditor?.editor;
if (editor) {
    editor.replaceRange('new text', from, to);
}

Exception: vault.adapter is acceptable for storage paths that Obsidian does not index or that must be addressed directly, including hidden legacy paths and configured Nexus data roots. Normalize paths first and keep storage roots settings-derived; do not hardcode .nexus.

API Best Practices

TaskDo ThisNot This
HTTP requestsrequestUrl()fetch()
Path handlingnormalizePath(userPath)Direct string concat
OS detectionPlatform.isMobile, Platform.isDesktopUser agent sniffing
File lookupvault.getFileByPath(path)Iterating vault.getFiles()
View accessworkspace.getActiveViewOfType(MarkdownView)workspace.activeLeaf.view

Commands

this.addCommand({
    id: 'my-action',  // Don't duplicate plugin ID
    name: 'My action',  // Sentence case, no "command" word
    // NO default hotkey - users set their own
    checkCallback: (checking) => {
        const view = this.app.workspace.getActiveViewOfType(MarkdownView);
        if (view) {
            if (!checking) {
                // Execute command
            }
            return true;
        }
        return false;
    }
});

Mobile Compatibility

import { Platform } from 'obsidian';

if (Platform.isMobile) {
    // Mobile-specific code
}

// ❌ These APIs are NOT available on mobile
import { fs, path, crypto } from 'node:*';  // Node.js modules
require('electron');  // Electron APIs

// ✅ Mobile alternatives
// Use SubtleCrypto instead of crypto
// Use navigator.clipboard instead of electron clipboard
// Set isDesktopOnly: true in manifest if Node.js required

Accessibility Requirements

// ✅ Interactive elements need aria-labels
const iconButton = container.createEl('button', { cls: 'icon-button' });
iconButton.setAttribute('aria-label', 'Open settings');
setIcon(iconButton, 'settings');

// ✅ Keyboard navigation (Tab, Enter, Space)
// ✅ Focus indicators with :focus-visible
// ✅ Touch targets minimum 44×44px on mobile

Code Quality Rules

RuleRequirement
Type safetyNo as any casts, use instanceof checks
VariablesUse const/let, never var
Console loggingNo console.log in production, only console.error for actual errors
UI textSentence case everywhere
CleanupRemove all template/sample code before submission

Manifest Requirements

{
    "id": "my-plugin-id",      // Lowercase, no "obsidian", doesn't end with "plugin"
    "name": "My Plugin Name",  // No "Obsidian" or "Plugin" suffix
    "version": "1.0.0",
    "minAppVersion": "1.0.0",
    "description": "Does something useful.",  // <250 chars, ends with punctuation
    "author": "Author Name",
    "isDesktopOnly": false     // true only if Node.js APIs required
}

Performance Guidelines

// ❌ Vault 'create' fires for ALL files on startup
this.registerEvent(this.app.vault.on('create', handler));

// ✅ Wait for layout ready
this.app.workspace.onLayoutReady(() => {
    this.registerEvent(this.app.vault.on('create', handler));
});

// OR check inside handler
onCreate(file: TFile) {
    if (!this.app.workspace.layoutReady) return;
    // Process event
}

References

<!-- sync:block:mockup-practice:start -->

Mockup Practice

Substantial UI work gets a standalone mockup in docs/mockups/ before any production code: new views, panels, modals, settings tabs, chat surfaces, and layout refactors. A tweak inside an existing layout does not — say so and implement.

Do not improvise the process from this note. The nexus-ui-mockups skill is the source of truth for how a mockup is built, revised, validated and handed off as a plan's visual contract — load it before starting, and follow its protocols and its check_mockup.py validator.

Implementation is a separate job under src/ and is governed by CLAUDE.md's hard rules (styles.css, registerDomEvent, no dynamic innerHTML). Mockup-only liberties never travel into plugin code.

<!-- sync:block:mockup-practice:end -->

Recent Milestones

March 2026

Mar 4: New Models + Bug Fixes ✅ (v4.4.5 → v4.4.6)

  • Added Codex Sonnet 4.6, Gemini 3.1 Pro/Flash Lite, GPT-5.3 Chat/Codex; removed legacy Codex 4 Opus/Sonnet
  • Fixed ConversationTitleModal focus trap: replaced fragile setTimeout/rAF hack, added focus restoration in onClose()
  • Fixed default temperature not loading from user settings (hardcoded 0.5 → reads defaultTemperature)
  • Fixed prompt selector using name instead of id as dropdown key (wouldn't persist selection)
  • Fixed syncWorkspacePrompt() saving name instead of id

February 2026

Feb 28: Dynamic Image Model Defaults ✅ (v4.4.4)

  • Image generation defaults now resolve from user settings instead of hardcoded values
  • Priority chain: explicit param > user settings > first available provider/model > fallback
  • generateImage: new resolveDefaults(), getAvailableProviderNames(), dynamic schema/errors
  • executePrompts/promptParser/RequestExecutor: removed hardcoded model lists and google-only restrictions
  • ImageGenerationService: added getInitializedProviders()
  • executeTypes: provider optional + accepts openrouter, model widened to string

Feb 26: Dynamic Image Model Loading ✅ (v4.4.3)

  • Image model dropdowns now load dynamically from adapters (removed hardcoded IMAGE_MODELS in ChatSettingsRenderer)
  • generateImage tool schema builds model enum at runtime from configured providers
  • Added getModelsForProvider() and getSupportedModelIds() to ImageGenerationService
  • Adding a new image model to an adapter now auto-populates UI and tool schema

Feb 26: New Image Models + FLUX Validation Fix ✅ (v4.4.2)

  • Added gemini-3.1-flash-image-preview (Nano Banana 2): Google direct + OpenRouter, 512px-4K, 14 ref images, new aspect ratios (1:4, 4:1, 1:8, 8:1)
  • Added gpt-5-image (OpenRouter only): GPT-5 with image generation, 400K context
  • Fixed FLUX models (flux-2-pro, flux-2-flex) failing validation — missing from supportedModels and ImageModel union type

Feb 23: Workspace Settings Display Fix ✅ (v4.4.1)

  • Root cause: renderWorkspacesTab() passed stale prefetchedWorkspaces (populated before SQLite ready) → WorkspacesTab skipped async load, showed JSONL fallback data only
  • Fix: pass prefetchedWorkspaces: null → WorkspacesTab always shows loading skeleton → awaits both workspaceService + hybridStorageAdapter → queries SQLite with full data
  • Also removed temporary [DEBUG-WS] console.error logs from 6 files: SettingsView.ts, WorkspacesTab.ts, ServiceManager.ts, WorkspaceService.ts, BackgroundProcessor.ts, PluginLifecycleManager.ts

Feb 22: Tool Call History Fix ✅ (v4.4.0)

  • MessageStreamHandler.ts — post-loop safety net: forces state=complete + accumulated toolCalls onto in-memory message before second save runs; prevents stale draft/null from overwriting good data
  • ConversationService.ts — try-catch around JSON.parse(tc.function.arguments) in convertToLegacyConversation; malformed JSON no longer crashes entire conversation load
  • MessageRepository.ts — defensive try-catch in rowToMessage() for toolCallsJson/metadataJson/alternativesJson
  • Note for existing stale conversations: Use the Nexus: Rebuild cache command to force SQLite cache rebuild from the synced event store; tool calls will reappear if present in JSONL.

Feb 22: TypeScript Build Fix ✅ (PR #31)

  • IPCTransportManager.ts — changed socket param from NodeJS.ReadWriteStream to net.Socket; removed 4 redundant casts
  • npm run build (tsc + esbuild) now passes clean

Feb 22: OpenAI CORS Bypass + Validation Fixes ✅ (PR #29)

  • nodeFetch.ts — Node.js https.request() passed as custom fetch to OpenAI SDK; bypasses CORS on /v1/responses
  • Fixed 3 stale validation probes: gpt-5-nano needs max_completion_tokens; Codex-3-5-haiku-latest deprecated → Codex-haiku-4-5-20251001
  • 619 tests (adds 55 nodeFetch unit tests)

Feb 22: Provider OAuth Connect ✅ (PR #26 — v4.3.4)

  • OpenAI Codex OAuth via ChatGPT: connect button, token refresh, model listing with (ChatGPT) suffix
  • Codex Responses API gotchas (CRITICAL):
    • No previous_response_id support — must use stateless full input array continuation
    • delta is plain string, not object — typeof event.delta === 'string' check required
    • CORS from app://obsidian.md — must use Node.js require('https').request() not fetch()
    • for await unreliable in Electron — use explicit chunkQueue/chunkWaiter event-listener queue
    • instructions field always required — cannot be conditional on conversationHistory
    • Model won't use tools without tool_choice: "auto" + explicit tool-use preamble in instructions
  • 547 tests passing, all console.log removed, JSON.parse in try-catch, expires_in validated

Feb 21: IPC Transport Fix ✅ (v4.3.2 — cherry-pick from PR #24, DylanLacey)

  • Fixed handleSocketConnection never wiring socket close/end to transport.close()
  • Crashed connectors no longer permanently wedge Protocol._transport; reconnects cleanly
  • FD leak on failed connect() also fixed (socket destroyed on rejection)
  • PR #24 mux part still open — pending contributor fix for hardcoded socket path

Feb 20: New Model Definitions ✅ (PR #22 — v4.3.1)

  • Added Codex Sonnet 4.6 (Codex-sonnet-4-6): 200K ctx, 64K out, $3/$15/M + 1M beta variant
  • Added Gemini 3.1 Pro Preview (gemini-3.1-pro-preview): 1M ctx, 65K out, $2/$12/M
  • Updated: AnthropicModels.ts, GoogleModels.ts, OpenRouterModels.ts

Feb 9: Conversation Memory Search ✅ (PR #19 — merged)

  • Semantic search across conversation turns and tool call traces via searchMemory tool
  • Two modes: Discovery (workspace-scoped) and Scoped (session-filtered, N-turn window)
  • QA pair model + ContentChunker (500-char/100-overlap) + sqlite-vec KNN + multi-signal reranking
  • Real-time indexing via ConversationEmbeddingWatcher + background backfill
  • Actionable error feedback, enhanced descriptions, optional workspaceId
  • EmbeddingService refactored: facade pattern (1034→199 lines) + 3 domain services
  • MemorySearchProcessor (824→553) and IndexingQueue (822→497) split into extracted modules
  • 351 tests pass (205 new), all coverage thresholds met, 19 commits
  • Plan: docs/plans/conversation-memory-search-plan.md
  • Review: docs/review/pr19-conversation-memory-search.md

Feb 5: Startup Performance Fix ✅ (PR #15)

  • Non-blocking startup ~200ms (75x improvement from ~15s)
  • Root cause: deadlock between ChatView.onOpen() and onLayoutReady
  • Solution: setTimeout(0) services, registerViewEarly(), non-blocking onOpen()

Feb 5: Chat Stop/Retry/Branch Bug Fixes ✅ (PR #16)

  • 12 bugs fixed across stop, retry, and branch navigation
  • Key patterns: clear-and-restream, incremental reconciliation, dual abort controllers
  • 142 unit tests across 7 new test files

Feb 5: Inline AI Editing Feature ✅ (PR #14)

  • Right-click or hotkey to edit selected text via LLM
  • State machine pattern, streaming preview, Jest test infrastructure (41 tests)

January 2026

Jan 24: ExecutePrompts improvements (optional provider/model, reference images, CommandManager cleanup) Jan 12: MCP integration settings fix (invalid config handling) Jan 4: CanvasManager agent (4 tools), SQLite transaction fix, memory leak fixes (7), embeddings toggle

December 2025

Dec 22: Subagent UI + architecture Dec 20: Auto-compaction + dual models + WebLLM Dec 17: Two-Tool Architecture (95% token reduction) Dec 16: Local embeddings + dead code cleanup (~4,000 lines removed) Dec 9: Mobile + branching persistence Dec 3: SQLite + JSONL hybrid storage

Quick Navigation

Core Directories

  • /src/agents/ - Agent implementations (PromptManager, ContentManager, etc.)
  • /src/services/ - Shared services (LLM providers, memory, conversations)
  • /src/components/ - UI components (chat view, settings, modals)
  • /src/types/ - TypeScript type definitions
  • /src/utils/ - Utility functions and helpers

Key Files

  • main.ts - Plugin entry point and lifecycle management
  • connector.ts - MCP server connector for Codex Desktop
  • src/agents/index.ts - Agent registry and initialization
  • src/services/conversationService.ts - Chat conversation management
  • src/services/llmService.ts - LLM provider abstraction layer

Agent Architecture

Available Agents

ToolManager (src/agents/toolManager/) - MCP Entry Point (Two-Tool Architecture)

  • getTools: Discovery - returns tool schemas for requested agents/tools
  • useTools: Execution - unified context-first tool execution
  • Only these 2 tools are exposed to Codex Desktop. All other agents work internally.

Tool names below are the CLI form (agent tool-name, kebab-case) — what a caller actually types and what getTools/useTools resolve. Verify against src/agents/** (slug:) before trusting; the /nexus-release skill gates on refreshing this list, and tests/unit/shippedGuidanceCommands.test.ts fails when shipped docs name a tool that does not exist.

  1. PromptManager (src/agents/promptManager/) - Custom prompts and LLM integration
    • prompt: execute, create, get, list, update, archive, sub, list-models, generate-image, generate-audio, generate-video, check-generated-artifact
    • No delete — the AI gets archive (reversible). Media generation is async: generate-* returns a job, poll check-generated-artifact <jobId>.
  2. ContentManager (src/agents/contentManager/) - Note reading/editing operations
    • content: read, write, replace, insert, set-property
    • read requires --start-line. replace is pattern-anchored {path, start, end, content} — anchor TEXT, not line numbers.
  3. StorageManager (src/agents/storageManager/) - File/folder management
    • storage: list, create-folder, move, copy, archive, open
  4. SearchManager (src/agents/searchManager/) - Advanced search operations
    • search: content, directory, memory, query-notes
  5. MemoryManager (src/agents/memoryManager/) - Workspace/state/workflow management
    • memory: create-workspace, list-workspaces, search-workspaces, load-workspace, update-workspace, archive-workspace, create-state, list-states, load-state, update-state, archive-state, run
    • No session tools — sessions are context fields, not tools. memory run triggers a workflow (--workflow-id/--workflow-name).
  6. CanvasManager (src/agents/canvasManager/) - Obsidian canvas operations
    • canvas: read, write, update, list
  7. TaskManager (src/agents/taskManager/) - Workspace-scoped project/task management with DAG dependencies
    • task: create-project, list-projects, update-project, archive-project, create, list, update, move, query, open, link-note
    • Note the asymmetry: project tools are suffixed (create-project), task tools are bare (create, list, update, move, query).
    • Services: TaskService (business facade), DAGService (pure computation)
    • Auto-loads task summary when workspace loads

Agent Structure Pattern

agents/
  [agentName]/
    [agentName].ts          # Main agent class extending BaseAgent
    tools/                   # Operation tools
      [toolName].ts         # File: read.ts, Class: ReadTool
      services/             # Tool-specific services
    services/               # Agent-level shared services
    types.ts                # Agent-specific types
    utils/                  # Agent-specific utilities

Base Classes

  • BaseAgent (src/agents/baseAgent.ts) - Common agent functionality
  • BaseTool (src/agents/baseTool.ts) - Common tool functionality with generic types
  • IAgent (src/agents/interfaces/IAgent.ts) - Agent interface contract
  • ITool (src/agents/interfaces/ITool.ts) - Tool interface contract

Current Context

Active Branch

main (current) — active worktrees:

  • .worktrees/fix-subagent-bugs (branch: fix/subagent-bugs) — pending manual test

Open PRs

#TitleStatus
#29OpenAI CORS bypass + validation probe fixesMerged ✅
#23Plugin store compliance auditAwaiting manual test in Obsidian before merge
#24Socket lifecycle fix (DylanLacey)Transport fix in main (v4.3.2); mux awaiting contributor socket path fix

Current Work

PR #23 — plugin store compliance (fix/plugin-store-audit-fixes): ready to merge, needs manual test PR (untracked) — subagent fixes (fix/subagent-bugs): 29 fixes, 372 tests passing, awaiting manual test

Branch Architecture

A branch IS a conversation with parent metadata:

  • metadata.parentConversationId: parent conversation
  • metadata.parentMessageId: message the branch is attached to
  • metadata.branchType: 'alternative' | 'subagent'

Key Files:

  • src/services/chat/BranchService.ts - Facade over ConversationService
  • src/ui/chat/controllers/SubagentController.ts - Subagent infrastructure
  • src/ui/chat/controllers/NexusLoadingController.ts - Loading overlays
  • src/ui/chat/services/ContextTracker.ts - Token/cost tracking

Known Issues

Workspace Delete Persistence (Feb 2):

  • Deleted workspaces may reappear on page reload
  • Backend delete logic looks correct, may be UI cache issue

Subagent Flow (Dec 22, fixed Feb 20 in fix/subagent-bugs — awaiting manual test):

  • 29 bugs fixed: icon race, retry-stuck, abort race, O(N) scan, Continue feature, EventBus instance-scoping, maxIterations enforcement, and more
  • Full fix list: docs/review/pr23-subagent-functionality-review.md

WebLLM/Nexus (Dec 20):

  • Multi-turn tool continuations may crash on Apple Silicon (WebGPU issue)
  • If startup hangs on "loading cache", clear site data

Backlog

  1. Obsidian Secrets API Adoption (target: March 2026): Migrate API key storage to SecretStorage API (v1.11.4+). Research: docs/preparation/obsidian-secrets-api-research.md
  2. Port 3000 conflict: Fixed OpenRouter OAuth port changed to 3456 (commit 15576fb2). MCPServer.ts HttpTransportManager still on 3000; long-term: make configurable.
  3. SOLID Audit: SystemPromptBuilder.ts and ModelAgentManager.ts are large files
  4. SQLiteCacheManager.ts (849 lines): Above 600-line threshold
  5. v5.0.0 Deprecation Cleanup: Remove backward compatibility for old dedicated agent structures (TODO(v5.0.0) in WorkspacePromptResolver)
  6. Obsidian CLI Integration (blocked: Catalyst-only): Research: docs/preparation/obsidian-cli-research.md
  7. Missing version-bump.mjs: package.json version lifecycle script references node version-bump.mjs but file doesn't exist — npm version will error. Either create it or remove the script reference.

Obsidian Plugin Guidelines Compliance

Status: All major issues addressed (Dec 2025). isDesktopOnly: false is correct (chat works on mobile, MCP requires desktop).

IssueStatus
innerHTML securityXSS fix applied (MessageEditController); 11 safe patterns remain
registerDomEventComplete (15 more migrated Feb 20)
console.log cleanup398 → 1 (ImageGenerationService.ts:55 pending)
Inline styles85 → 13
Type safety (as any)0 remaining (all 12 removed Feb 20)
@ts-ignore1 remaining (documented)

Development Notes

Build Commands

  • npm run dev - Development build with watch mode
  • npm run build - Production build (TypeScript + esbuild)
  • npm run test - Run Jest test suite
  • npm run lint - Run ESLint
  • npm run deploy - Build and deploy via PowerShell script

Testing Approach

  • Unit Tests: Jest for core logic and services (619 tests total — 351 baseline + 268 OAuth/Codex/nodeFetch)
  • Integration Tests: Manual testing in Obsidian environment
  • MCP Testing: Via Codex Desktop connection

Code Patterns

  • Agents: Extend BaseAgent, register tools in constructor
  • Tools: Extend BaseTool<Params, Result>, implement execute(), getParameterSchema(), getResultSchema()
  • Results: Return { success: boolean, ...data } or { success: false, error: string }
  • Services: Singletons with dependency injection via constructor

Dependencies

See package.json. Key: MCP SDK, LLM provider SDKs (Anthropic, OpenAI, Google, Groq), express, winston, uuid.

Code Quality

SOLID Refactoring Progress (12/17 large files completed)

Key reductions: HybridStorageAdapter (-72%), LLMService (-75%), ChatService (-60%), LLMProviderModal (-82%), MessageBubble (-45%), EmbeddingService (-81% facade), MemorySearchProcessor (-33%), IndexingQueue (-40%)

Remaining Large Files (600+ lines)

ChatSettingsModal (702), ChatView (659), OpenRouterAdapter (640), ValidationService (625), BatchExecutePromptTool (618), GoogleAdapter (612)

MCP Integration

Server Configuration

  • Server runs locally via connector.js
  • Configured in Codex Desktop's claude_desktop_config.json
  • Server identifier: claudesidian-mcp-[vault-name]
  • Supports multiple vault instances simultaneously

Two-Tool Architecture

Instead of 50+ tools, MCP exposes just 2: getTools (discovery) and useTools (execution).

Context Schema: { workspaceId, sessionId, memory, goal, constraints? } - all required except constraints.

Flow: getTools → get schemas → useTools with the context fields at the top level plus a single tool string.

⚠️ The calls: [{agent, tool, params}] array and the nested context: {...} object were removed in v5.9.0 and are rejected outright (Deprecated payload shape).

Benefits: 95% token reduction (~15,000 → ~500), works with small context models.

Key Files: src/agents/toolManager/ (agent + tools), src/services/trace/ToolCallTraceService.ts

Tool Count: 60 tools across 9 agents (not counting ToolManager meta-tools)

Memory & Workspace System

Storage Location

Primary synced event store: settings-derived vault root, settings.storage.rootPath (default Nexus) with managed data under <rootPath>/data/:

  • conversations/<conversationId>/shard-*.jsonl - sharded append-only conversation events
  • workspaces/<workspaceId>/shard-*.jsonl - sharded append-only workspace/session/state/trace events
  • tasks/<workspaceId>/shard-*.jsonl - sharded append-only task/project events
  • _meta/ - storage and migration manifests

Configured root rules: resolve with resolveVaultRoot(settings, { configDir }); never hardcode Nexus except as DEFAULT_STORAGE_SETTINGS.rootPath, and never hardcode .nexus for new writes.

Legacy read paths: .obsidian/plugins/<plugin-folder>/data/, compatibility plugin folders (nexus, claudesidian-mcp), legacy .nexus/, and storage.previousRootPaths remain read/migration fallback sources. They are not the primary write target.

Local-only cache (auto-rebuilt from JSONL, never source of truth):

  • Desktop: IndexedDB-backed via IndexedDBCacheBlobStore
  • Mobile: vault.adapter file backend at .obsidian/plugins/<plugin-folder>/data/cache.db

Architecture

  • Hybrid JSONL + SQLite: sharded JSONL event store = source of truth, SQLite = rebuildable fast query/vector cache
  • True database pagination with OFFSET/LIMIT
  • Workspace-scoped sessions and traces
  • Searchable via MemoryManager and SearchManager agents

UI Components

  • Chat View: src/components/ChatView.ts - conversations, branching, streaming, tool accordion
  • Settings: src/components/ConfigModal.ts - tabbed LLM/agent configuration

Chat Suggesters

TriggerPurpose
/Tool hints
@Custom agents
[[Note links
#Workspace data

Key files: src/ui/chat/components/suggesters/, MessageEnhancer.ts, SystemPromptBuilder.ts

Architectural Notes

  • Subagents: Branch → stream via LLMService → save result. chunk.toolCalls are display-only.
  • WebLLM/Nexus: Nexus Quark (4B, 4K context), <tool_call> format. May crash on Apple Silicon.
  • Storage: Branches as JSONL events, SQLite v4 schema, tool names use agent_tool format.
  • Apps & Vault Access: App agents now receive the full Obsidian App via BaseAppAgent.setApp(), which also wires in Vault. Use getVault() for file writes and getApp() when a tool needs workspace, command, or Web Viewer access. Use vault.createBinary() for binary outputs (audio, images, PDFs) and vault.create() for text files. Always ensure parent directories exist before writing. Follow the patterns established by ElevenLabs audio tools and the webTools app. For generated artifacts where the caller must choose the destination, prefer a required outputPath parameter.

Working Memory

<!-- Auto-managed by pact-memory skill. Last 5 memories shown. Full history searchable via pact-memory skill. --><!-- SESSION_START -->

Current Session

<!-- Auto-managed by session_init hook. Overwritten each session. -->
  • Resume: Codex --resume f2266b8f-b5ca-4b90-a87e-db0a0304ffd7
  • Team: pact-f2266b8f
  • Started: 2026-03-08 11:52:33 UTC
<!-- SESSION_END -->

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.