## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# KOGRAL Documentation

Welcome to the KOGRAL documentation! This directory contains comprehensive documentation for KOGRAL (KOwledge GRAphs, Local-first), built with mdBook.

## 📚 Reading the Documentation

You have several options for reading the documentation:

### Option 1: Serve Locally with mdBook (Recommended)

The best reading experience with navigation, search, and live reload:

    # Serve documentation at http://localhost:3000
    just docs::serve

This will:

- Build the mdBook
- Start a local web server on port 3000
- Open your browser automatically
- Watch for changes and auto-reload

### Option 2: Build Static HTML

Generate static HTML files you can browse offline:

    # Build mdBook to docs/book/
    just docs::build

Then open `docs/book/index.html` in your browser.

### Option 3: Read Markdown Files Directly

All documentation is written in Markdown and can be read directly:

- Browse via GitHub/GitLab web interface
- Use your editor's Markdown preview
- Read from terminal with `bat`, `glow`, or similar tools

Navigation: See SUMMARY.md for the complete table of contents.

## 🛠️ Documentation Commands

We use `just` recipes for documentation tasks. All commands assume you're in the project root directory.

### Build and Serve

    # Serve documentation locally (recommended)
    just docs::serve

    # Build static HTML
    just docs::build

    # Watch and rebuild on file changes
    just docs::watch

### Validation

    # Test code examples in documentation
    just docs::test

    # Check for broken links
    just docs::check-links

### Cleanup

    # Remove build artifacts
    just docs::clean

### View All Documentation Commands

    just docs::help

## 📦 Installing mdBook

mdBook is required to build and serve the documentation.

### Install via Cargo

    cargo install mdbook

### Install Optional Tools

For enhanced functionality:

    # Link checker (validates internal/external links)
    cargo install mdbook-linkcheck

    # Mermaid diagram support
    cargo install mdbook-mermaid

    # PlantUML diagram support
    cargo install mdbook-plantuml

### Verify Installation

    mdbook --version
    # Should output: mdbook v0.4.x or later

## 📖 Documentation Structure

The documentation is organized into the following sections:

### 1. KOGRAL Definition (`kogral/`)

- What is KOGRAL and why it exists
- Core concepts (nodes, edges, graphs)
- Design philosophy

### 2. Guides (`guides/`)

- Quick start (5 minutes)
- Installation guide
- Use cases with examples

### 3. Architecture (`architecture/`)

- System overview with diagrams
- Config-driven architecture
- Graph model details
- ADRs (Architectural Decision Records)

### 4. Setup (`setup/`)

- Initial setup
- Development environment
- Production deployment
- Testing environment
- CI/CD integration

### 5. Configuration (`config/`)

- Configuration overview
- Nickel schema reference
- Runtime configuration
- Environment modes (dev/prod/test)

### 6. Storage (`storage/`)

- Storage architecture (hybrid strategy)
- Filesystem backend
- SurrealDB backend
- In-memory backend
- Sync mechanism

### 7. AI & Embeddings (`ai/`)

- Semantic search
- Embedding providers
- Provider comparison
- Configuration examples

### 8. Templates (`templates/`)

- Template system (Tera)
- Document templates
- Export templates
- Custom templates

### 9. CLI Reference (`cli/`)

- All kb-cli commands
- Common workflows
- Advanced usage
- Troubleshooting

### 10. Apps & Integrations (`apps/`)

- MCP quick guide (Claude Code)
- Logseq integration
- Vapora integration

### 11. API Reference (`api/`)

- MCP tools specification
- Storage trait
- Embedding trait
- REST API (future)

### 12. Contributing (`contributing/`)

- Development setup
- Code guidelines
- Testing standards
- Documentation guidelines

## 🎨 Visual Diagrams

The documentation includes SVG diagrams for visual understanding:

- architecture-overview.svg - Complete system architecture
- core-concepts.svg - Node types and relationships
- config-composition.svg - Configuration flow (Nickel → JSON → Rust)
- storage-architecture.svg - Hybrid storage strategy

These diagrams are embedded in relevant documentation pages and can be viewed standalone in a browser.

## 🔍 Searching the Documentation

When using `just docs::serve`, you get a built-in search feature:

1. Click the search icon (🔍) in the top-left corner
2. Type your query
3. Press Enter to navigate results

The search indexes all documentation content including:

- Page titles
- Headers
- Body text
- Code examples (optionally)

## ✏️ Editing Documentation

### File Format

All documentation is written in GitHub Flavored Markdown with mdBook extensions.

See contributing/documentation.md for detailed editing guidelines.

### Adding a New Page

1. Create the markdown file in the appropriate directory
2. Add it to `SUMMARY.md` for navigation
3. Build to verify: `just docs::build`

### Adding a New Section

1. Create the directory
2. Add a `README.md` for the section landing page
3. Add section to `SUMMARY.md`

## 🧪 Testing Documentation

### Test Code Examples

    just docs::test

This runs all Rust code examples in the documentation to ensure they compile.

### Check Links

    just docs::check-links

This validates all internal and external links.

## 📝 Documentation Standards

When contributing to documentation:

1. Use clear, concise language - Write for developers and AI agents
2. Include code examples - Show, don't just tell
3. Add diagrams where helpful - Visual aids improve understanding
4. Link related concepts - Help readers discover related content
5. Test code examples - Ensure code compiles and works
6. Use consistent formatting - Follow existing page structure
7. Update SUMMARY.md - New pages must be in navigation
8. Run checks before committing:

    just docs::build
    just docs::test
    just docs::check-links

## 💡 Tips

### Live Reload While Writing

    just docs::watch

This watches for changes and rebuilds automatically. Open http://localhost:3000 in your browser to see updates in real-time.

### Markdown Preview in Editor

Most editors have Markdown preview:

- VS Code: `Ctrl+Shift+V` (Cmd+Shift+V on Mac)
- IntelliJ/CLion: Preview pane (right side)
- Vim/Neovim: Use plugins like `markdown-preview.nvim`

### Quick Reference

- SUMMARY.md - Table of contents (edit to add/remove pages)
- book.toml - mdBook configuration
- theme/ - Custom CSS/JS (if needed)
- diagrams/ - SVG diagrams

## 🐛 Troubleshooting

### mdbook command not found

    # Install mdBook
    cargo install mdbook

    # Verify installation
    mdbook --version

### Port 3000 already in use

    # Serve on different port
    cd docs
    mdbook serve --port 3001

### Links broken after moving files

    # Check all links
    just docs::check-links

    # Update internal links in affected files
    # Then rebuild
    just docs::build

## 📚 Resources

- mdBook User Guide
- GitHub Flavored Markdown Spec
- Markdown Guide

## 🤝 Contributing to Documentation

Documentation improvements are always welcome! To contribute:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test with `just docs::build` and `just docs::test`
5. Submit a pull request

See contributing/documentation.md for detailed guidelines.

Happy documenting! 📖

If you have questions or need help, please open an issue or reach out to the maintainers.

# What is KOGRAL?

KOGRAL (KOwledge GRAphs, Local-first) is a git-native knowledge graph system designed for developer teams to capture, connect, and query structured knowledge.

## Purpose

KOGRAL solves the problem of knowledge fragmentation in software development:

- 📝 Notes scattered across tools
- 🤔 Decisions lost in chat histories
- 📚 Guidelines buried in wikis
- 🔄 Patterns rediscovered repeatedly
- 🤖 AI agents lacking project context

## Solution

KOGRAL provides a unified, queryable knowledge graph that:

1. Captures knowledge in structured, git-friendly markdown
2. Connects concepts through typed relationships
3. Queries via text and semantic similarity
4. Integrates with AI tools (Claude Code via MCP)
5. Syncs across local (filesystem) and shared (SurrealDB) storage

## Core Philosophy

### Config-Driven

Behavior defined in Nickel schemas, not hardcoded:

    {
      graph = { name = "my-project" },
      storage = { primary = 'filesystem },
      embeddings = { provider = 'fastembed },
      templates = { templates_dir = "templates" },
    }

Every aspect configurable: storage, embeddings, templates, query behavior.

### Git-Friendly

Knowledge stored as markdown files with YAML frontmatter:

    ---
    id: note-123
    type: note
    title: Error Handling Patterns
    tags: [rust, error-handling]
    ---

    # Error Handling Patterns

    Use `thiserror` for custom error types...

Changes tracked via git, reviewable in PRs, mergeable across branches.

### AI-Native

Built for agent collaboration:

- MCP Protocol: Native integration with Claude Code
- Semantic Search: Find concepts, not just keywords
- Auto-Linking: Suggest relationships based on content
- Context Injection: Agents query relevant guidelines before coding

## Key Concepts

### Nodes

6 types of knowledge nodes:

Type | Purpose | Example
--- | --- | ---
Note | General observations | "Rust ownership patterns"
Decision | ADRs (Architectural Decision Records) | "Use SurrealDB for storage"
Guideline | Code standards | "Error handling with thiserror"
Pattern | Reusable solutions | "Repository pattern for DB access"
Journal | Daily reflections | "2026-01-17 progress notes"
Execution | Agent task records | "Implemented auth module"

### Relationships

6 typed edges connecting nodes:

Relation | Meaning | Example
--- | --- | ---
`relates_to` | Conceptual link | Note ↔ Note
`depends_on` | Prerequisite | Pattern → Guideline
`implements` | Concrete realization | Code → Pattern
`extends` | Inheritance | ProjectGuideline → BaseGuideline
`supersedes` | Replacement | DecisionV2 → DecisionV1
`explains` | Documentation | Note → Execution

### Multi-Graph Architecture

Local Graph (per project):

- Stored in `.kogral/` directory
- Git-tracked for version control
- Project-specific knowledge

Shared Graph (organization-wide):

- Centralized guidelines and patterns
- SurrealDB for scalability
- Inherited by projects

Inheritance Resolution:

    Shared Guidelines (priority: 50)
        ↓
    Project Guidelines (priority: 100)
        ↓
    Effective Guidelines (higher priority wins)

## Use Cases

### For Developers

- Capture decisions as you make them (ADRs)
- Document patterns for future reference
- Track daily progress in journal entries
- Query past decisions before new implementations

### For Teams

- Share guidelines across projects
- Standardize patterns organization-wide
- Onboard new members with searchable knowledge
- Review decisions in context with git history

### For AI Agents

- Query guidelines before generating code
- Check past decisions for context
- Document executions for audit trails
- Suggest related patterns during implementation

## Comparison with Other Tools

### vs. Logseq

Feature | KOGRAL | Logseq
--- | --- | ---
Storage | Git-friendly markdown + DB | Local markdown
AI Integration | Native MCP protocol | Plugin-based
Config | Type-safe Nickel schemas | JSON files
Multi-Backend | Filesystem + SurrealDB | Filesystem only
Semantic Search | Multiple AI providers | Local only
Graph Queries | SurrealDB graph queries | Block references

Compatibility: KOGRAL can import/export Logseq graphs for visual editing.

### vs. Obsidian

Feature | KOGRAL | Obsidian
--- | --- | ---
Target Audience | Developers + AI agents | Knowledge workers
Focus | Structured knowledge graph | Flexible note-taking
Configuration | Config-driven (Nickel) | Settings UI
CLI | Full CLI + MCP server | Limited CLI
Version Control | Git-native | Git plugin

Use Case: KOGRAL for developer knowledge, Obsidian for personal notes.

### vs. Notion/Confluence

Feature | KOGRAL | Notion/Confluence
--- | --- | ---
Storage | Local-first | Cloud-only
Format | Plain markdown | Proprietary
AI Access | Programmatic API | Web scraping
Offline | Full functionality | Limited
Privacy | Self-hosted | Third-party servers

Advantage: KOGRAL keeps sensitive knowledge on-premises.

## Architecture Overview

    ┌─────────────────────────────────────────────────────┐
    │                    kb-core                          │
    │  (Rust library: graph, storage, config, query)     │
    └──────────────┬─────────────┬────────────────────────┘
                   │             │
         ┌─────────┴──────┐  ┌──┴─────────┐
         │                │  │            │
    ┌────▼─────┐  ┌──────▼──▼───┐  ┌─────▼─────┐
    │  kb-cli  │  │   kb-mcp     │  │  NuShell  │
    │ (13 cmds)│  │ (MCP server) │  │ (scripts) │
    └──────────┘  └──────────────┘  └───────────┘

Layers:

1. kb-core: Core library (models, storage, query)
2. kb-cli: Command-line interface
3. kb-mcp: MCP server for AI integration
4. Scripts: NuShell automation (sync, backup, etc.)

## When to Use KOGRAL

✅ Good fit:

- Developer teams building software
- Projects with architectural decisions
- Organizations standardizing patterns
- AI-assisted development workflows
- Knowledge requiring version control

❌ Not ideal for:

- Personal journaling (use Obsidian)
- Team wikis (use Notion)
- Customer documentation (use mdBook)
- Simple note-taking (use Logseq)

## Next Steps

- New to KOGRAL? → Installation Guide
- Ready to start? → Quick Start
- Want examples? → Use Cases
- Understand design? → Architecture Overview

> "Knowledge is only valuable when it's accessible, connected, and queryable."

# Core Concepts

Understanding the fundamental concepts behind KOGRAL.

## The Knowledge Graph

At its core, KOGRAL is a directed graph where:

- Nodes = pieces of knowledge (notes, decisions, guidelines, patterns)
- Edges = typed relationships between concepts

This graph structure enables:

- Discovery: Find related concepts through traversal
- Context: Understand how ideas connect
- Evolution: Track how knowledge changes over time

## Node Types

### 1. Note

Purpose: Capture general observations, learnings, and discoveries.

When to use:

- Documenting a concept you learned
- Recording implementation details
- Capturing meeting notes
- Quick knowledge capture

Example:

    ---
    type: note
    title: Async Trait Patterns in Rust
    tags: [rust, async, patterns]
    ---

    # Async Trait Patterns in Rust

    Using async traits with the async-trait crate...

### 2. Decision (ADR)

Purpose: Record architectural decisions with full context.

When to use:

- Choosing between alternatives (REST vs GraphQL)
- Major technical decisions (database selection)
- Trade-off analysis
- Explaining "why" for future reference

Structure:

- Context: Background and problem
- Decision: What was chosen
- Consequences: Positive and negative outcomes
- Alternatives: What was considered but rejected

Example:

    ---
    type: decision
    title: Use SurrealDB for Storage
    status: accepted
    ---

    ## Context
    Need a graph database that supports our relationship model...

    ## Decision
    Adopt SurrealDB as the primary storage backend.

    ## Consequences
    + Better graph query performance
    + Native relationship support
    - Additional infrastructure dependency
    - Team learning curve

### 3. Guideline

Purpose: Define coding standards, best practices, and conventions.

When to use:

- Code style rules
- Architecture patterns to follow
- Security requirements
- Testing standards

Can be:

- Shared: Organization-wide (in shared KOGRAL)
- Project-specific: Overrides shared guidelines

Example:

    ---
    type: guideline
    language: rust
    category: error-handling
    ---

    # Rust Error Handling Guidelines

    1. Use `thiserror` for custom error types
    2. Never use `unwrap()` in production code
    3. Always propagate errors with `?`

### 4. Pattern

Purpose: Document reusable solutions to common problems.

When to use:

- Recurring implementation patterns
- Tested solutions
- Best practice implementations
- Code templates

Structure:

- Problem: What challenge does this solve?
- Solution: The pattern/approach
- Context: When to use/not use
- Example: Working code

Example:

    ---
    type: pattern
    title: Repository Pattern for Database Access
    tags: [architecture, database, pattern]
    ---

    ## Problem
    Need consistent database access across modules.

    ## Solution
    Repository pattern with trait abstraction...

    ## Example
    \`\`\`rust
    trait UserRepository {
        async fn find_by_id(&self, id: Uuid) -> Result<User>;
    }
    \`\`\`

### 5. Journal

Purpose: Daily development log for tracking progress and reflections.

When to use:

- End of day summaries
- Daily standup notes
- Progress tracking
- Blocker documentation

Auto-linked: KOGRAL can auto-link journal entries to mentioned concepts.

Example:

    ---
    type: journal
    date: 2026-01-17
    ---

    ## Progress
    - Implemented authentication module
    - Fixed cache race condition

    ## Blockers
    - Need API versioning discussion

    ## Learnings
    - tokio::select! perfect for timeouts

### 6. Execution

Purpose: Record AI agent execution results (from Vapora integration).

When to use:

- Agent task completion
- Execution metrics
- Agent decision history
- Audit trail

Example:

    ---
    type: execution
    task_type: code_generation
    agent: rust-expert
    outcome: success
    duration: 45s
    ---

    Generated authentication module following project guidelines.

## Relationship Types

### 1. `relates_to`

Meaning: General conceptual relationship.

Use: Connect related ideas without specific dependency.

Example:

    [Note: Async Patterns] --relates_to--> [Note: Tokio Runtime]

### 2. `depends_on`

Meaning: Prerequisite relationship. Source requires target to exist/be understood first.

Use: Learning paths, implementation order.

Example:

    [Pattern: Advanced Error Handling] --depends_on--> [Guideline: Basic Errors]

### 3. `implements`

Meaning: Concrete implementation of an abstract concept.

Use: Connect code to patterns/guidelines.

Example:

    [Note: Auth Module Implementation] --implements--> [Pattern: Repository Pattern]

### 4. `extends`

Meaning: Inheritance/extension relationship.

Use: Guideline overrides, pattern variations.

Example:

    [Guideline: Project Error Handling] --extends--> [Guideline: Shared Error Handling]

### 5. `supersedes`

Meaning: Replacement relationship. Source replaces target.

Use: Track evolution of decisions/patterns.

Example:

    [Decision: Use GraphQL v2] --supersedes--> [Decision: Use REST]

### 6. `explains`

Meaning: Documentation/clarification relationship.

Use: Connect notes to implementations, executions to rationale.

Example:

    [Note: Why We Chose Rust] --explains--> [Decision: Adopt Rust]

## Multi-Graph Architecture

KB supports multiple knowledge graphs:

### Local Graph (Project-Specific)

Location: `.kogral/` in project directory

Purpose: Project-specific knowledge

- Project decisions
- Implementation notes
- Local patterns
- Daily journals

Storage: Filesystem (git-tracked)

Scope: Single project

### Shared Graph (Organization-Wide)

Location: Configurable (e.g., `~/org/.kogral-shared`)

Purpose: Shared organizational knowledge

- Coding guidelines
- Standard patterns
- Architecture principles
- Security policies

Storage: SurrealDB (centralized) or filesystem (synced)

Scope: All projects

### Inheritance Model

    Shared Guidelines (priority: 50)
            ↓ [inherited by]
    Project Guidelines (priority: 100)
            ↓ [effective]
    Combined Guidelines (higher priority wins)

Example:

Shared guideline: "Use Result<T> for errors" Project override: "Use Result<T> + log all errors with tracing" Effective: Both rules apply, project adds requirement

## Config-Driven Behavior

Everything is configurable via Nickel schemas:

    {
      graph = { name = "my-project" },          # Graph metadata
      storage = { primary = 'filesystem },       # Where to store
      embeddings = { provider = 'fastembed },    # AI provider
      templates = { /* ... */ },                 # Document templates
      query = { similarity_threshold = 0.6 },    # Search behavior
    }

No hardcoding: Change behavior without code changes.

## Semantic Search

Beyond keyword matching, KOGRAL uses embeddings to find concepts:

Keyword search: "Find 'error handling'"

- Matches exact phrase "error handling"

Semantic search: "How to handle failures gracefully?"

- Finds: error handling, exception patterns, Result types, panic recovery
- Understands: "failures" = errors, "gracefully" = best practices

How it works:

1. Text → Embedding (384 or 1536 dimensional vector)
2. Similarity search (cosine distance)
3. Return nodes above threshold (e.g., 0.6)

Providers:

- fastembed: Local, free, offline (384d)
- OpenAI: Cloud, best quality (1536d)
- Claude: Cloud, excellent (1024d)
- Ollama: Self-hosted (768d)

## Templates

Tera templates generate consistent documents:

    ---
    id: {{ id }}
    type: {{ type }}
    title: {{ title }}
    tags: [{% for tag in tags %}"{{ tag }}"{% endfor %}]
    ---

    # {{ title }}

    {{ content }}

Customizable: Override templates per project.

Export formats: Logseq, JSON, Markdown.

## MCP Integration

Model Context Protocol connects KOGRAL to Claude Code:

    Claude Code
        ↓ [JSON-RPC 2.0]
    kb-mcp Server
        ↓ [Rust API]
    kb-core Library
        ↓ [Storage]
    Knowledge Graph

Tools: `kogral/search`, `kogral/add_note`, `kogral/get_guidelines`, etc.

Resources: `kogral://project/notes`, `kogral://shared/guidelines`

Prompts: `kogral/summarize_project`, `kogral/find_related`

## Git-Friendly Storage

Markdown + YAML frontmatter = git-tracked knowledge:

    ---
    id: note-123
    type: note
    title: My Note
    ---

    # Content here

Benefits:

- ✅ Diffs in PRs (reviewable changes)
- ✅ Branches (experiment with knowledge)
- ✅ Merges (combine knowledge from feature branches)
- ✅ History (track evolution over time)
- ✅ Blame (who added this knowledge?)

## Logseq Content Blocks

KOGRAL provides full support for Logseq-style outliner blocks with rich metadata and structure.

### What are Blocks?

Blocks are the fundamental unit of content in Logseq's outliner format:

- Each bullet point is a block
- Blocks can have children (nested blocks)
- Blocks support tasks, tags, and custom properties
- Blocks can reference other blocks or pages

### Block Features

Task Status:

    - TODO Implement authentication #high-priority
    - DOING Write tests
    - DONE Deploy to staging
    - LATER Consider GraphQL API
    - NOW Fix critical bug
    - WAITING Code review from @alice
    - CANCELLED Old approach

Inline Tags:

    - Learning Rust ownership #rust #learning #card
      - Prevents data races at compile time
      - Borrowing rules enforce safety

Custom Properties:

    - Research paper summary
      priority:: high
      reviewed:: 2026-01-17
      source:: https://example.com/paper.pdf
      - Key findings...

Block and Page References:

    - Meeting notes from [[2026-01-17]]
      - Discussed architecture ((block-ref-123))
      - Action items: [[Project Roadmap]]

Hierarchical Structure:

    - Parent block #top-level
      - Child block 1
        - Nested child
      - Child block 2

### Configuration

Blocks support is opt-in via configuration:

    {
      blocks = {
        enabled = true,              # Enable blocks parsing
        parse_on_import = true,      # Auto-parse from Logseq imports
        serialize_on_export = true,  # Serialize to outliner format
        enable_mcp_tools = true,     # Enable block-related MCP tools
      },
    }

### Use Cases

1. Task Management:

    - TODO Weekly sprint planning #meeting
      - DONE Review last sprint
      - DOING Plan current sprint
      - TODO Assign tasks

2. Flashcards (Spaced Repetition):

    - What is Rust's ownership model? #card #rust
      - Ownership prevents data races at compile time
      - Each value has a single owner
      - When owner goes out of scope, value is dropped

3. Knowledge Capture with Metadata:

    - Tokio async runtime patterns #rust #async
      category:: architecture
      difficulty:: intermediate
      - Use tokio::select! for concurrent operations
      - spawn_blocking() for CPU-intensive work

4. Linked Notes:

    - Discussed [[ADR-001]] in architecture meeting
      - Decided on SurrealDB
      - See ((meeting-notes-block-id)) for details

### Block Queries (MCP Tools)

Query blocks across your knowledge base:

Find blocks by tag:

    {
      "tool": "kogral/find_blocks",
      "arguments": { "tag": "card" }
    }

Find all TODOs:

    {
      "tool": "kogral/find_todos",
      "arguments": { "limit": 20 }
    }

Find flashcards:

    {
      "tool": "kogral/find_cards",
      "arguments": { "limit": 10 }
    }

Find blocks by property:

    {
      "tool": "kogral/find_blocks",
      "arguments": {
        "property_key": "priority",
        "property_value": "high"
      }
    }

### Architecture

Hybrid Model:

- Content stored as markdown string (source of truth)
- Blocks lazily parsed on first access
- Cached block structure for fast queries
- Bidirectional: markdown ↔ blocks

BlockParser:

- Parse outliner markdown → Block structures
- Serialize Block structures → outliner markdown
- Preserve all metadata (tags, status, properties, references)
- Round-trip fidelity for Logseq compatibility

Storage:

- Filesystem: markdown with blocks inline
- SurrealDB: dedicated `block` table with indexes
- Indexes: tags, status, parent_id, full-text search

See Also:

- ADR-004: Logseq Blocks Support
- Logseq Blocks Design
- MCP Block Tools

## Key Principles

1. Capture During Work: Don't wait, document as you go
2. Link as You Learn: Connect related concepts immediately
3. Query When Needed: AI-assisted discovery of relevant knowledge
4. Evolve Over Time: Update decisions, supersede patterns
5. Share Wisely: Shared guidelines, local specifics

## Next Steps

- Understand motivation: Why KOGRAL?
- Learn philosophy: Design Philosophy
- See architecture: System Overview
- Start using: Quick Start

# Why KB?

Understanding the motivation behind the Knowledge Base system.

## The Problem: Knowledge Fragmentation

Modern software development generates enormous amounts of knowledge:

📝 Notes: Implementation details, learnings, discoveries 🤔 Decisions: Why we chose X over Y 📚 Guidelines: How we write code, architecture patterns 🔄 Patterns: Reusable solutions we've discovered 💭 Discussions: Slack threads, meeting notes, PR comments 🐛 Bug Fixes: How we solved issues

But where does this knowledge live?

- Meeting notes in Google Docs
- Decisions buried in Slack threads
- Guidelines in a wiki nobody updates
- Patterns in someone's head
- PR discussions lost in GitHub history
- Bug solutions in closed Jira tickets

The result?

❌ Rediscovering solutions to problems we've already solved ❌ Inconsistent practices because guidelines aren't accessible ❌ Slow onboarding as new developers lack context ❌ Lost context when team members leave ❌ Repeated mistakes because past lessons aren't preserved

## The Solution: Unified, Queryable Knowledge Graph

Knowledge Base provides a single source of truth for project knowledge:

✅ Capture once: Notes, decisions, guidelines, patterns ✅ Connect concepts: Typed relationships between ideas ✅ Query naturally: Text and semantic search ✅ AI-assisted: Claude Code integration via MCP ✅ Git-tracked: Version control for knowledge ✅ Shared wisdom: Organization-wide guidelines + project-specific

## What Makes KB Different?

### 1. Git-Native

Other tools: Notion, Confluence, wikis in web apps

KB: Markdown files in `.kogral/` directory

Benefits:

    # Review knowledge changes in PRs
    git diff .kogral/decisions/

    # Branch knowledge with code
    git checkout feature-branch
    # .kogral/ follows the branch

    # Merge knowledge from feature work
    git merge feature-branch
    # Knowledge merges like code

Result: Knowledge versioned alongside code it describes.

### 2. AI-Native

Other tools: Manual search, browse folders

KB: AI queries via Claude Code

Example:

    You: "Find anything about error handling"

    Claude: [Searches semantically, not just keywords]
    Found 5 concepts:
    - Pattern: Error Handling with thiserror
    - Guideline: Result Type Best Practices
    - Decision: Use anyhow for Application Errors
    - Note: Custom Error Types
    - Journal: Fixed error propagation bug

    [All related, even without exact keyword match]

Result: Find concepts, not just documents with keywords.

### 3. Config-Driven

Other tools: Hardcoded behavior, limited customization

KB: Nickel schemas define everything

Example:

    # Development: local embeddings, no costs
    { embeddings = { provider = 'fastembed } }

    # Production: cloud embeddings, best quality
    { embeddings = { provider = 'openai, model = "text-embedding-3-large" } }

Result: Adapt behavior without code changes.

### 4. Multi-Graph

Other tools: One knowledge base per project

KB: Shared organizational knowledge + project-specific

Structure:

    Organization Shared KB
      ├── Rust Guidelines (applies to all projects)
      ├── Security Patterns (applies to all projects)
      └── Testing Standards (applies to all projects)

    Project A KB
      ├── Inherits shared guidelines
      ├── Project-specific decisions
      └── Can override shared guidelines

    Project B KB
      ├── Inherits same shared guidelines
      ├── Different project decisions
      └── Different overrides

Result: Consistency across organization, flexibility per project.

### 5. Structured Relationships

Other tools: Backlinks, tags, folders

KB: Typed relationships with meaning

Example:

    Pattern: Repository Pattern
        ↑ [implements]
    Note: User Service Implementation
        ↑ [relates_to]
    Decision: Use PostgreSQL
        ↑ [depends_on]
    Guideline: Database Access Patterns

Result: Understand how knowledge connects, not just that it connects.

## Real-World Impact

### Before KB

New developer joins:

1. Read outdated wiki (2 hours)
2. Ask teammates for context (1 hour per question × 10 questions)
3. Discover guidelines by reading code (days)
4. Miss important decisions (leads to mistakes)

Time to productivity: 2-4 weeks

### With KB

New developer joins:

1. `kb search "architectural decisions"` → finds all ADRs
2. Ask Claude: "What are our coding guidelines?" → gets current guidelines with inheritance
3. Browse related notes via graph traversal
4. Full context available, no tribal knowledge

Time to productivity: 3-5 days

### Before KB

Team makes decision:

1. Discussion in Slack (lost after a week)
2. Someone documents in wiki (maybe)
3. 6 months later: "Why did we choose X?" → nobody remembers
4. Re-debate the same decision

### With KB

Team makes decision:

1. Discussion captured as ADR during meeting
2. Context, decision, consequences documented
3. Linked to related patterns and guidelines
4. 6 months later: `kb show decision-use-x` → full context instantly

### Before KB

Solving a bug:

1. Encounter race condition in cache
2. Debug for 2 hours
3. Fix it
4. Solution lost in PR comments

Two months later: Different developer, same bug, 2 more hours

### With KB

Solving a bug:

1. Encounter race condition
2. Ask Claude: "Have we seen cache race conditions before?"
3. Claude finds journal entry from 2 months ago with solution
4. Apply fix in 10 minutes

Two months later: Same query, same instant solution

## Who Benefits?

### Individual Developers

✅ Personal knowledge base: Capture learnings, build expertise ✅ Quick recall: "How did I solve this before?" ✅ Context switching: Return to old projects with full context ✅ Career growth: Document what you learn, portfolio of knowledge

### Teams

✅ Shared context: Everyone has access to team knowledge ✅ Onboarding: New members ramp up faster ✅ Consistency: Follow shared guidelines and patterns ✅ Collaboration: Build on each other's knowledge

### Organizations

✅ Institutional memory: Knowledge persists beyond individual tenure ✅ Best practices: Standardize across teams ✅ Compliance: Document security and architecture decisions ✅ Efficiency: Stop solving the same problems repeatedly

### AI Agents

✅ Context injection: Agents query guidelines before generating code ✅ Decision awareness: Agents check past decisions ✅ Pattern following: Agents use documented patterns ✅ Execution tracking: Agent actions documented automatically

## When NOT to Use KB

KB is not ideal for:

❌ Personal journaling: Use Obsidian or Logseq ❌ Team wikis: Use Notion or Confluence ❌ Customer docs: Use mdBook or Docusaurus ❌ Project management: Use Jira or Linear ❌ Code comments: Use inline documentation

KB is perfect for:

✅ Developer knowledge graphs ✅ Architectural decision records ✅ Pattern libraries ✅ Coding guidelines ✅ Technical context ✅ AI-assisted development

## The Vision

Today's development:

    Developer → writes code → commits

With KB:

    Developer → writes code → documents decisions → links to patterns → commits code + knowledge
                        ↓
                AI Agent queries knowledge → generates better code
                        ↓
                Team discovers patterns → reuses solutions → faster development

Knowledge becomes an active participant in development, not an afterthought.

## Design Philosophy

Three core principles drive KB:

### 1. Knowledge should be captured during work, not after

❌ "I'll document this later" → never happens ✅ "Claude, document this decision" → done in 30 seconds

### 2. Knowledge should be connected, not isolated

❌ Standalone documents in folders ✅ Graph of interconnected concepts

### 3. Knowledge should be queryable, not browsable

❌ "Let me look through 50 docs to find..." ✅ "Find anything related to error handling" → instant semantic results

## Get Started

Ready to stop losing knowledge?

- Understand concepts: Core Concepts
- Learn philosophy: Design Philosophy
- Quick start: Quick Start Guide
- See examples: Use Cases

> "The best time to document was during implementation. The second best time is now."

But with KB + AI, "now" is instant.

# Design Philosophy

The principles and values guiding Knowledge Base design and implementation.

## Core Tenets

### 1. Config-Driven, Not Hardcoded

Principle: All behavior should be configurable via schemas, not baked into code.

Why: Flexibility without code changes. Users adapt KB to their workflows, not vice versa.

Example:

    #![allow(unused)]
    fn main() {
    // ❌ Bad: Hardcoded
    impl EmbeddingProvider {
        fn new() -> Self {
            FastEmbedProvider::new("BAAI/bge-small-en-v1.5") // Can't change
        }
    }

    // ✅ Good: Config-driven
    impl EmbeddingProvider {
        fn from_config(config: &EmbeddingConfig) -> Result<Box<dyn EmbeddingProvider>> {
            match config.provider {
                'fastembed => Ok(Box::new(FastEmbedProvider::new(&config.model)?)),
                'openai => Ok(Box::new(OpenAIProvider::new(&config.model)?)),
            }
        }
    }
    }

Benefits:

- Switch embedding providers without recompilation
- Different configs for dev/prod
- User choice, not developer mandate

### 2. Type-Safe Configuration

Principle: Validate configuration before runtime, not during.

Why: Catch errors early, reduce runtime failures.

Implementation: Nickel contracts + serde validation = double validation

    # Schema defines valid values
    EmbeddingProvider = [| 'openai, 'claude, 'fastembed |]

    # Typo caught at export time, not runtime
    { provider = 'opena1 }  # Error: Invalid variant

Benefits:

- Errors found during `nickel export`, not during execution
- Self-documenting: schema is the spec
- Refactoring safe: change schema, find all usages

### 3. Local-First, Cloud-Optional

Principle: Core functionality works offline, cloud is enhancement.

Why: Privacy, cost control, offline development.

Examples:

Feature | Local | Cloud
--- | --- | ---
Storage | Filesystem | SurrealDB
Embeddings | fastembed | OpenAI/Claude
Search | Text-based | Semantic

Benefits:

- Works on planes, trains, areas with poor internet
- No API costs for small projects
- Privacy-sensitive projects keep data local
- Production can use cloud for scale

### 4. Git-Friendly by Default

Principle: Knowledge should version alongside code.

Why: Knowledge describes code, should evolve with it.

Implementation:

- Markdown + YAML frontmatter (text-based, diffable)
- One file per node (granular commits)
- Wikilinks preserved (works in Logseq, Obsidian)

Benefits:

    # Review knowledge changes in PRs
    git diff .kogral/decisions/

    # Knowledge follows branches
    git checkout feature-x
    # .kogral/ reflects feature-x decisions

    # Knowledge merges
    git merge feature-x
    # Merge conflicts = knowledge conflicts (resolve intentionally)

### 5. AI-Native, Human-Readable

Principle: Optimized for AI consumption, readable by humans.

Why: Best of both worlds - AI-assisted discovery, human verification.

Implementation:

- Structured: YAML frontmatter for AI parsing
- Semantic: Embeddings for AI queries
- Readable: Markdown for human consumption
- Linked: Typed relationships for AI traversal

Example:

    ---
    id: note-auth
    type: note
    title: Authentication Implementation
    tags: [security, auth]
    relates_to: [guideline-security, pattern-jwt]
    ---

    # Authentication Implementation

    Humans read this markdown normally.

    AI can:
    - Parse frontmatter for metadata
    - Extract tags for filtering
    - Follow relates_to links
    - Generate embeddings for semantic search

### 6. Composition Over Inheritance

Principle: Build systems by composing small, focused components.

Why: Flexibility, testability, maintainability.

Implementation:

    #![allow(unused)]
    fn main() {
    // Small, focused traits
    trait Storage { ... }
    trait EmbeddingProvider { ... }
    trait TemplateEngine { ... }

    // Compose into systems
    struct KnowledgeBase {
        storage: Box<dyn Storage>,
        embeddings: Option<Box<dyn EmbeddingProvider>>,
        templates: TemplateEngine,
    }
    }

Benefits:

- Swap storage without affecting embeddings
- Disable embeddings without breaking storage
- Test components in isolation
- Add new providers by implementing trait

### 7. Fail Fast, Fail Clearly

Principle: Detect errors early, provide clear messages.

Why: Developer experience - fast feedback, actionable errors.

Implementation:

    #![allow(unused)]
    fn main() {
    // ❌ Bad: Silent failure
    fn load_config(path: &Path) -> Option<Config> {
        std::fs::read_to_string(path)
            .ok()
            .and_then(|s| serde_json::from_str(&s).ok())
    }

    // ✅ Good: Explicit errors
    fn load_config(path: &Path) -> Result<Config, ConfigError> {
        let content = std::fs::read_to_string(path)
            .map_err(|e| ConfigError::ReadFailed(path.to_path_buf(), e))?;

        serde_json::from_str(&content)
            .map_err(|e| ConfigError::ParseFailed(path.to_path_buf(), e))
    }
    }

Error messages:

    ❌ "Failed to load config"
    ✅ "Failed to read config file '/path/to/config.ncl': Permission denied"

### 8. Convention Over Configuration (With Escape Hatches)

Principle: Sane defaults, but everything customizable.

Why: Easy to start, flexible as you grow.

Examples:

    # Minimal config (uses conventions)
    { graph = { name = "my-project" } }
    # Defaults: filesystem storage, no embeddings, standard templates

    # Full config (explicit everything)
    {
      graph = { name = "my-project" },
      storage = { primary = 'surrealdb, /* ... */ },
      embeddings = { provider = 'openai, /* ... */ },
      templates = { templates_dir = "custom" },
    }

Conventions:

- `.kogral/` directory in project root
- Filesystem storage by default
- YAML frontmatter + markdown body
- Standard template names (note.md.tera, decision.md.tera)

Escape hatches:

- `--kb-dir` to use different location
- Configure alternative storage backends
- Custom frontmatter schemas
- Override any template

### 9. Documentation as Code

Principle: Documentation lives with code, versioned together.

Why: Outdated docs are worse than no docs.

Implementation:

- ADRs in `.kogral/decisions/` (alongside code)
- Guidelines in `.kogral/guidelines/` (versioned with code)
- Patterns in `.kogral/patterns/` (evolve with implementations)

Benefits:

    # Code and docs branch together
    git checkout old-version
    # .kogral/ reflects that version's decisions

    # Code and docs merge together
    git merge feature
    # Merge includes new patterns, updated guidelines

    # Code and docs reviewed together
    # PR shows code + decision + guideline updates

### 10. Optimize for Discoverability

Principle: Knowledge is useless if you can't find it.

Why: The point is to use knowledge, not just store it.

Features:

- Text search: Find exact keywords
- Semantic search: Find related concepts
- Graph traversal: Follow relationships
- Tag filtering: Narrow by category
- MCP integration: AI-assisted discovery

Example:

User doesn't remember exact term, but knows the concept:

    "Find anything about handling errors gracefully"

KB finds (semantically):

- "Error Handling with thiserror" (pattern)
- "Result Type Best Practices" (guideline)
- "Panic Recovery" (note)
- "Graceful Degradation" (pattern)

No exact keyword match needed, concept match sufficient.

### 11. Build for Humans, Enable AI

Principle: Humans are the primary users, AI is the assistant.

Why: AI should enhance human workflows, not replace them.

Implementation:

- Human-readable formats: Markdown, YAML
- Human-editable: Any text editor works
- Human-discoverable: `ls .kogral/notes/` shows files
- AI-enhanced: MCP for AI-assisted queries

Example:

    # Human workflow
    vim .kogral/notes/my-note.md    # Edit directly
    git add .kogral/notes/my-note.md
    git commit -m "Add note about X"

    # AI-enhanced workflow
    # (in Claude Code)
    "Add a note about X with tags Y, Z"
    # AI creates file, human reviews

### 12. Embrace the Graph

Principle: Knowledge is interconnected, embrace the relationships.

Why: Context comes from connections, not isolation.

Implementation:

- Typed relationships (not just "related")
- Bidirectional traversal
- Relationship strength (0.0-1.0)
- Multi-hop queries

Example:

    Find all patterns that:
      - Are implemented by current project
      - Depend on shared guidelines
      - Were added in the last 6 months

    # Graph query, not sequential file search

## Anti-Patterns to Avoid

### 1. ❌ Hardcoding Behavior

    #![allow(unused)]
    fn main() {
    // Don't
    const EMBEDDING_MODEL: &str = "BAAI/bge-small-en-v1.5";

    // Do
    let model = config.embeddings.model.as_str();
    }

### 2. ❌ Runtime Schema Validation

    // Don't validate at runtime
    let provider = config.provider; // Might be invalid

    // Do validate at export time (Nickel contracts)
    provider | [| 'openai, 'claude, 'fastembed |]

### 3. ❌ Opaque Errors

    #![allow(unused)]
    fn main() {
    // Don't
    Err("Failed".into())

    // Do
    Err(KbError::NodeNotFound(id.to_string()))
    }

### 4. ❌ Coupling Components

    #![allow(unused)]
    fn main() {
    // Don't
    impl KnowledgeBase {
        fn search(&self) -> Vec<Node> {
            let embeddings = FastEmbedProvider::new(); // Hardcoded!
            // ...
        }
    }

    // Do
    impl KnowledgeBase {
        fn search(&self) -> Vec<Node> {
            if let Some(provider) = &self.embeddings {
                // Use configured provider
            }
        }
    }
    }

### 5. ❌ Proprietary Formats

    // Don't
    Binary blob: [0x4B, 0x42, 0x01, ...]

    // Do
    Markdown + YAML:
    ---
    id: note-1
    ---
    # Content

## Influences

KB draws inspiration from:

- Logseq: Outliner, graph view, wikilinks
- Obsidian: Markdown-first, local storage, plugins
- Zettelkasten: Atomic notes, links, emergence
- ADR: Decision records, context preservation
- Git: Version control, branching, merging
- Nickel: Type-safe configuration, contracts
- MCP: AI integration protocol
- SurrealDB: Graph database, relationships

## Implementation Principles

### Rust

- Zero unsafe code (`#![forbid(unsafe_code)]`)
- No `unwrap()` in production code
- Always use `Result<T>` for fallibility
- Comprehensive error types with `thiserror`
- Full test coverage (100+ tests)

### Nickel

- Contracts for validation
- Defaults in schemas
- Documentation in contracts
- Composition via imports

### NuShell

- Structured data pipelines
- Error handling in scripts
- Colored output for UX
- Dry-run modes

## Evolution Strategy

KB follows these guidelines for evolution:

1. Backward Compatibility: Don't break existing configs
2. Deprecation Period: Warn before removal (1 major version)
3. Migration Tools: Provide automated migrations
4. Semantic Versioning: MAJOR.MINOR.PATCH strictly

## Conclusion

These principles guide every decision:

✅ Config-driven: Behavior in schemas, not code ✅ Type-safe: Validate before runtime ✅ Local-first: Works offline, cloud optional ✅ Git-friendly: Knowledge versions with code ✅ AI-native: Optimized for AI, readable by humans ✅ Composable: Small pieces, loosely coupled ✅ Fast feedback: Fail early, clear errors ✅ Discoverable: Easy to find what you need

The goal: Knowledge management that developers actually use.

## Next Steps

- See it in action: Use Cases
- Understand architecture: System Overview
- Start using: Quick Start

# Installation

This guide covers installing and setting up the KOGRAL.

## Prerequisites

### Required

- Rust 1.70 or later

      curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

- Nickel CLI for configuration management

      cargo install nickel-lang-cli

### Optional

- NuShell for maintenance scripts

      cargo install nu

- SurrealDB for scalable storage backend

      # macOS
      brew install surrealdb/tap/surreal

      # Linux/Windows
      curl -sSf https://install.surrealdb.com | sh

## Installation Methods

### Method 1: Install from Source (Recommended)

    # Clone the repository
    git clone <repository-url> knowledge-base
    cd knowledge-base

    # Build the workspace
    cargo build --release

    # Install CLI tool
    cargo install --path crates/kb-cli

    # Verify installation
    kb --version

### Method 2: Build Specific Crates

    # Build only kb-core library
    cargo build --package kb-core --release

    # Build only kb-cli
    cargo build --package kb-cli --release

    # Build only kogral-mcp server
    cargo build --package kb-mcp --release

### Method 3: Build with Features

    # Build with all features (filesystem + SurrealDB + fastembed)
    cargo build --workspace --all-features --release

    # Build with specific features
    cargo build --package kb-core --features "surrealdb,fastembed" --release

## Feature Flags

kb-core supports optional features:

Feature | Description | Default
--- | --- | ---
`filesystem` | Filesystem storage backend | ✅ Yes
`surrealdb` | SurrealDB storage backend | ❌ No
`fastembed` | Local embedding generation | ❌ No
`full` | All features enabled | ❌ No

Example usage:

    # Enable SurrealDB backend
    cargo build --features surrealdb

    # Enable all features
    cargo build --features full

## Environment Setup

### 1. Initialize a Knowledge Base

    # Navigate to your project
    cd /path/to/your/project

    # Initialize .kb directory
    kb init

    # Or initialize with custom name
    kb init --name "My Project" --description "Project knowledge base"

This creates:

    your-project/
    └── .kogral/
        ├── config.toml
        ├── notes/
        ├── decisions/
        ├── guidelines/
        ├── patterns/
        └── journal/

### 2. Configure Nickel Schemas (Optional)

If you want advanced configuration:

    # Copy default config
    cp /path/to/knowledge-base/config/defaults.ncl .kogral/config.ncl

    # Edit configuration
    $EDITOR .kogral/config.ncl

    # Export to TOML (for kb-cli compatibility)
    nickel export --format json .kogral/config.ncl | kb config import

### 3. Set Up Shared Knowledge Base (Optional)

For shared guidelines and patterns across projects:

    # Create shared KB location
    mkdir -p ~/Tools/.kogral-shared
    cd ~/Tools/.kogral-shared

    # Initialize shared KB
    kb init --name "Shared Knowledge" --description "Cross-project guidelines"

    # Configure inheritance in project
    kb config set inheritance.base ~/Tools/.kogral-shared

## Verify Installation

### Test CLI

    # Check version
    kb --version

    # Show help
    kb --help

    # Test initialization (dry-run)
    kb init --dry-run

### Test MCP Server

    # Start MCP server in test mode
    kb serve --transport stdio

    # In another terminal, test with echo
    echo '{"jsonrpc":"2.0","id":1,"method":"kogral/search","params":{"query":"test"}}' | kb serve

### Run Tests

    # Run all tests
    cargo test --workspace

    # Run integration tests
    cargo test --package kb-core --test '*'

    # Run with all features
    cargo test --workspace --all-features

## SurrealDB Setup (Optional)

If using SurrealDB backend:

### Start SurrealDB Server

    # Start server
    surreal start --log trace --user root --pass root file://data.db

    # Or use memory backend for testing
    surreal start --log trace --user root --pass root memory

### Configure kb-core

Edit `.kogral/config.ncl`:

    {
      storage = {
        primary = 'filesystem,
        secondary = {
          enabled = true,
          type = 'surrealdb,
          url = "ws://localhost:8000",
          namespace = "kb",
          database = "default",
          username = "root",
          password = "root",
        },
      },
    }

### Initialize Schema

    # Run migration script
    nu scripts/kb-migrate.nu --target latest

    # Or manually import schema
    surreal import --conn ws://localhost:8000 --user root --pass root --ns kb --db default schema.surql

## Embedding Provider Setup (Optional)

### Local fastembed

    # Build with fastembed feature
    cargo build --package kb-core --features fastembed

    # Models will be downloaded on first use

### API Providers (OpenAI, Claude, Ollama)

Set environment variables:

    # OpenAI
    export OPENAI_API_KEY="sk-..."

    # Claude (Anthropic)
    export ANTHROPIC_API_KEY="sk-ant-..."

    # Ollama (local)
    export OLLAMA_API_BASE="http://localhost:11434"

Configure provider in `.kogral/config.ncl`:

    {
      embeddings = {
        enabled = true,
        provider = 'openai,  # or 'claude, 'ollama, 'fastembed
        model = "text-embedding-3-small",
        api_key_env = "OPENAI_API_KEY",
      },
    }

## Claude Code Integration

To use kb-mcp with Claude Code:

### 1. Build MCP Server

    cargo build --package kb-mcp --release

### 2. Configure Claude Code

Add to `~/.config/claude/config.json`:

    {
      "mcpServers": {
        "kogral-mcp": {
          "command": "/path/to/knowledge-base/target/release/kb-mcp",
          "args": ["serve"],
          "env": {}
        }
      }
    }

### 3. Test Connection

Start Claude Code and verify:

    > kb/search "test"

## Troubleshooting

### Nickel Not Found

    # Verify nickel is installed
    nickel --version

    # If not, install
    cargo install nickel-lang-cli

    # Add cargo bin to PATH
    export PATH="$HOME/.cargo/bin:$PATH"

### Compilation Errors

    # Update Rust
    rustup update stable

    # Clean and rebuild
    cargo clean
    cargo build --workspace

### SurrealDB Connection Failed

    # Check SurrealDB is running
    curl http://localhost:8000/health

    # Start SurrealDB with correct settings
    surreal start --bind 0.0.0.0:8000 --user root --pass root memory

### MCP Server Not Responding

    # Test stdio communication
    echo '{"jsonrpc":"2.0","id":1,"method":"ping"}' | kb serve

    # Check logs
    kb serve --log-level debug

## Next Steps

- Quick Start Guide - Create your first knowledge base
- Configuration Reference - Customize behavior
- MCP Tools - Integrate with Claude Code

## Uninstallation

    # Remove installed binary
    cargo uninstall kb-cli

    # Remove project knowledge base
    rm -rf /path/to/project/.kogral

    # Remove shared knowledge base
    rm -rf ~/Tools/.kogral-shared

# Quick Start Guide

Get up and running with the KOGRAL in 5 minutes.

## Prerequisites

- Rust 1.70+ installed
- Nickel CLI installed (`cargo install nickel-lang-cli`)
- kb-cli installed (see Installation)

## Step 1: Initialize Your Knowledge Base

Navigate to your project directory and initialize:

    cd /path/to/your/project
    kb init

This creates a `.kogral/` directory with the following structure:

    .kogral/
    ├── config.toml       # Configuration
    ├── notes/            # General notes
    ├── decisions/        # Architectural decisions
    ├── guidelines/       # Project guidelines
    ├── patterns/         # Reusable patterns
    └── journal/          # Daily journal entries

## Step 2: Create Your First Note

Add a note to your knowledge base:

    kb add note "Getting Started with Rust" \
      --tags rust,programming,learning \
      --content "Key concepts for learning Rust effectively."

Or create interactively:

    kb add note
    # Follow the prompts to enter title, tags, and content

## Step 3: Create a Decision Record

Document an architectural decision:

    kb add decision "Use SurrealDB for Storage" \
      --context "Need scalable storage for knowledge graph" \
      --decision "Adopt SurrealDB for its graph capabilities" \
      --consequence "Better query performance" \
      --consequence "Additional infrastructure dependency"

## Step 4: Link Nodes Together

Create relationships between nodes:

    # Find node IDs
    kb list

    # Create a relationship
    kb link <note-id> <decision-id> relates_to

Available relationship types:

- `relates_to` - General conceptual link
- `depends_on` - Dependency relationship
- `implements` - Implementation of a concept
- `extends` - Inheritance/extension
- `supersedes` - Replaces older version
- `explains` - Documentation/clarification

## Step 5: Search Your Knowledge Base

Search by text:

    # Simple search
    kb search "rust"

    # Filter by type
    kb search "architecture" --type decision

    # Limit results
    kb search "error handling" --limit 5

Search semantically (requires embeddings):

    # Semantic search finds conceptually related content
    kb search "memory safety" --semantic --threshold 0.7

## Step 6: View Node Details

    # Show node by ID
    kb show <node-id>

    # Show node with relationships
    kb show <node-id> --with-relationships

## Step 7: Edit Documents Directly

All knowledge base documents are markdown files you can edit:

    # Open in your editor
    $EDITOR .kogral/notes/getting-started-with-rust.md

    # Or use your favorite markdown editor
    code .kogral/notes/

Document format:

    ---
    id: unique-id
    type: note
    title: Getting Started with Rust
    created: 2026-01-17T10:30:00Z
    modified: 2026-01-17T10:30:00Z
    tags: [rust, programming, learning]
    status: active
    ---

    # Getting Started with Rust

    Content goes here with [[wikilinks]] to other nodes.

    ## Key Concepts

    - Ownership
    - Borrowing
    - Lifetimes

## Step 8: Create a Daily Journal Entry

Start journaling your development progress:

    kb add journal "Today's Progress" \
      --content "Learned about trait objects and dynamic dispatch in Rust."

Or open today's journal directly:

    $EDITOR .kogral/journal/$(date +%Y-%m-%d).md

## Step 9: Export to Logseq (Optional)

If you use Logseq, export your knowledge base:

    nu scripts/kb-export-logseq.nu /path/to/logseq-graph

This creates a Logseq-compatible graph you can open in Logseq for visual editing.

## Step 10: Start MCP Server for Claude Code

Integrate with Claude Code for AI-assisted knowledge management:

    # Start MCP server
    kb serve

    # Configure in ~/.config/claude/config.json
    # Then use in Claude Code:
    # > kb/search "rust ownership"

## Common Workflows

### Capture Quick Notes

    # Quick note
    kb add note "Remember to check error handling in parser module" --tags todo,parser

### Document Architectural Decisions

    # Create ADR
    kb add decision "Adopt Async Rust for I/O Operations" \
      --status accepted \
      --tags architecture,async

### Build a Pattern Library

    # Add a pattern
    kb add pattern "Error Handling with thiserror" \
      --tags rust,error-handling \
      --content "Standard pattern for error types in this project."

### Track Daily Progress

    # Add journal entry
    kb add journal --content "Implemented search functionality. Need to add semantic search next."

## Next Steps

### Customize Configuration

Edit `.kogral/config.ncl` for advanced configuration:

    {
      graph = {
        name = "My Project",
        version = "1.0.0",
      },

      embeddings = {
        enabled = true,
        provider = 'fastembed,
      },

      templates = {
        templates_dir = "templates",
      },
    }

See Configuration Reference for all options.

### Set Up Shared Guidelines

Create a shared knowledge base for organization-wide standards:

    # Create shared KB
    mkdir -p ~/Tools/.kogral-shared
    cd ~/Tools/.kogral-shared
    kb init --name "Shared Guidelines"

    # Add guidelines
    kb add guideline "Rust Error Handling" \
      --language rust \
      --category error-handling

    # Configure inheritance in projects
    kb config set inheritance.base ~/Tools/.kogral-shared

### Automate with NuShell Scripts

    # Backup regularly
    nu scripts/kb-backup.nu --compress

    # Sync with SurrealDB
    nu scripts/kb-sync.nu --direction bidirectional

    # Generate statistics
    nu scripts/kb-stats.nu --show-tags

### Integrate with Git

    # Add to version control
    git add .kogral/
    git commit -m "docs: Add knowledge base"

    # Add to .gitignore (optional: exclude certain types)
    echo ".kogral/journal/" >> .gitignore

## Tips and Tricks

### Use Wikilinks

Link to other nodes naturally in markdown:

    See [[getting-started-with-rust]] for basics.
    Related decision: [[use-surrealdb-for-storage]].

### Reference Code

Link to specific code locations:

    Error handling implementation: @src/parser.rs:42

### Tag Consistently

Use consistent tagging for better searchability:

    # Good tagging
    --tags rust,error-handling,pattern

    # Avoid
    --tags Rust,ErrorHandling,patterns

### Leverage Templates

Customize templates for your workflow:

    # Copy template
    cp templates/note.md.tera templates/meeting-notes.md.tera

    # Edit for meeting notes format
    $EDITOR templates/meeting-notes.md.tera

## Troubleshooting

### "KB directory not found"

    # Make sure you initialized
    kb init

    # Or specify KB directory
    kb --kb-dir /path/to/.kb search "query"

### "Node not found"

    # List all nodes to find ID
    kb list

    # Search for node
    kb search "partial title"

### "Failed to parse frontmatter"

Check your markdown file has valid YAML frontmatter:

    ---
    id: my-note
    type: note
    title: My Note
    ---

## Further Reading

- Configuration Reference - Full configuration options
- CLI Commands - All available commands
- Document Format - Markdown and frontmatter details
- MCP Tools - Claude Code integration

## Getting Help

- Check `kb --help` for command usage
- Read inline help: `kb add --help`
- Report issues on GitHub
- Join community discussions

Congratulations! You've created your first knowledge base. Start capturing knowledge and building connections.

# Daily Workflows

# Use Cases

Real-world scenarios showing how KOGRAL solves common development challenges.

## Use Case 1: Onboarding New Team Members

### Problem

New developer joins the team. Needs to understand:

- Why we chose SurrealDB over PostgreSQL
- How error handling works in this project
- What coding patterns we follow
- Recent architectural decisions

### Solution with KOGRAL

Team lead:

    # Export project knowledge to Logseq for visual exploration
    kb export logseq ~/onboarding-docs

New developer (with Claude Code):

    Summarize our architectural decisions from the last 6 months

Claude (via MCP):

    Found 8 decisions:

    1. **Use SurrealDB for Storage** (accepted, 3 months ago)
       - Context: Need graph database for knowledge relationships
       - Consequences: Better queries, additional infrastructure

    2. **Adopt Rust for Core Logic** (accepted, 5 months ago)
       - Context: Type safety and performance critical
       - Consequences: Steeper learning curve, excellent reliability

    ... [6 more decisions]

New developer:

    What are our error handling guidelines?

Claude (via `kb/get_guidelines`):

    ## Rust Error Handling Guidelines

    ### Project Guidelines (priority: 150)
    - Use thiserror for custom error types
    - Never unwrap() in production
    - Propagate with ? operator

    ### Shared Guidelines (priority: 50)
    - Always use Result<T> for fallible operations
    - Add context with .context()

    [Links to full guideline documents]

Result: New developer productive in days, not weeks.

## Use Case 2: Architectural Decision Records (ADR)

### Problem

Team discussing whether to add Redis caching. Multiple opinions, need to document the decision for future reference.

### Solution with KOGRAL

During meeting:

    Document decision: Add Redis caching for API responses

    Context:
    - Current database queries average 200ms
    - 80% of queries are repeated within 5 minutes
    - Need sub-50ms response times for user experience

    Decision:
    - Add Redis with 5-minute TTL for GET endpoints
    - Cache invalidation on POST/PUT/DELETE

    Consequences:
    - ✅ Expected 80% reduction in database load
    - ✅ Sub-50ms response times achieved
    - ❌ Additional infrastructure (Redis server)
    - ❌ Cache consistency challenges
    - ❌ Debugging complexity increases

    Status: proposed (pending infrastructure setup)

Claude (creates ADR):

    Created: decision-add-redis-caching
    Type: decision
    Status: proposed

    Added relationships:
      - Relates to: note-performance-requirements
      - Depends on: decision-use-docker-compose (infrastructure)

    Decision documented and linked to related knowledge.

Two months later, someone asks "Why Redis?":

    kb show decision-add-redis-caching

Full context available instantly.

## Use Case 3: Pattern Library

### Problem

Team solves the same problems repeatedly:

- How to structure error types
- Database connection patterns
- Testing strategies

Each developer rediscovers solutions.

### Solution with KOGRAL

First time solving error handling:

    kb add pattern "Error Handling with thiserror" \
      --tags rust,error-handling,pattern \
      --content "$(cat docs/error-pattern.md)"

Next time someone needs error handling:

    I'm implementing a new module with errors. What's our standard pattern?

Claude (via `kogral/search`):

    Found pattern: Error Handling with thiserror

    ## Pattern: Error Handling with thiserror

    ### Problem
    Need structured, type-safe error handling with good error messages.

    ### Solution
    ```rust
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum ModuleError {
        #[error("Database connection failed: {0}")]
        Database(#[from] sqlx::Error),

        #[error("Validation failed: {0}")]
        Validation(String),
    }

### Usage

    #![allow(unused)]
    fn main() {
    fn do_work() -> Result<(), ModuleError> {
        let conn = connect_db()?; // Auto-converts via #[from]
        validate_input()?;
        Ok(())
    }
    }

[Full pattern with examples]

Would you like me to create a new error module following this pattern?

    **Result**: Consistent error handling across codebase, zero time wasted rediscovering patterns.

    ---

    ## Use Case 4: Daily Development Journal

    ### Problem

    Developer wants to track:
    - What they worked on each day
    - Blockers encountered
    - Solutions discovered
    - Ideas for future work

    ### Solution with KOGRAL

    **End of each day**:

    ```bash
    kb add journal "2026-01-17" --content "
    ## Progress
    - Implemented user authentication
    - Fixed race condition in cache layer
    - Started work on API rate limiting

    ## Blockers
    - SurrealDB migration tool not working with nested objects
    - Need to discuss API versioning strategy with team

    ## Learnings
    - tokio::select! is perfect for timeout patterns
    - serde_json::Value useful for dynamic JSON handling

    ## Tomorrow
    - Finish rate limiting implementation
    - Review PR #234
    - Team meeting: discuss API versioning
    "

With Claude Code (even easier):

    End of day journal:

    Implemented authentication, fixed cache race condition, started rate limiting.
    Blocked on SurrealDB migrations and need API versioning discussion.
    Learned about tokio::select! for timeouts.
    Tomorrow: finish rate limiting, review PR #234, versioning meeting.

Claude (creates journal entry):

    Created journal entry: 2026-01-17

    Tags auto-detected: authentication, cache, rate-limiting, surrealdb

    Linked to:
      - pattern-timeout-handling (implements)
      - decision-use-surrealdb (relates_to)

    Journal entry saved.

Two weeks later (forgot how you solved that race condition):

    How did I fix the cache race condition?

Claude (semantic search):

    From your journal entry on 2026-01-17:

    "Fixed race condition in cache layer by using DashMap instead of
    RwLock<HashMap>. DashMap provides lock-free concurrent access."

    [Link to full journal entry]

Result: Development history preserved, solutions easy to recall.

## Use Case 5: Code Review Preparation

### Problem

Preparing for code review. Need to:

- Document why certain decisions were made
- Link implementation to requirements
- Explain trade-offs to reviewers

### Solution with KOGRAL

Before creating PR:

    I just implemented the caching layer.
    Add a note documenting the implementation and link it to our caching decision.

Claude (via `kb/add_note` + `kb/link`):

    Created note: note-caching-layer-implementation

    Content:
    # Caching Layer Implementation

    ## Approach
    - Redis with 5-minute TTL
    - Cache-aside pattern
    - Invalidation on mutations

    ## Trade-offs
    - Chose simplicity over advanced features (no LRU eviction)
    - 5-minute TTL balances freshness vs cache hit rate
    - Cache-aside pattern chosen over write-through (simpler)

    ## Testing
    - Unit tests for cache logic
    - Integration tests with real Redis
    - Load test: 1000 req/s sustained

    Links:
      - Implements: decision-add-redis-caching
      - Follows: pattern-cache-aside
      - Tests: guideline-integration-testing

    Note added and linked to decision-add-redis-caching.

In PR description:

    ## Implementation

    Implements caching layer per decision-add-redis-caching.

    See note-caching-layer-implementation for detailed rationale.

    ## Trade-offs

    [Copy from note]

Reviewers:

    kb show decision-add-redis-caching
    kb show note-caching-layer-implementation

Full context available, review focuses on code quality not "why this approach?"

## Use Case 6: Cross-Project Guidelines

### Problem

Organization has 10 projects. Each should follow:

- Same Rust error handling patterns
- Same security guidelines
- Same testing standards

Currently: duplicated wikis, inconsistent practices.

### Solution with KOGRAL

Setup shared KOGRAL:

    # Create organization-wide shared KB
    mkdir -p ~/org/.kogral-shared
    cd ~/org/.kogral-shared
    kb init --name "Organization Guidelines"

    # Add shared guidelines
    kb add guideline "Rust Error Handling" \
      --language rust \
      --category error-handling \
      --content "$(cat org-rust-errors.md)"

    kb add guideline "Security Best Practices" \
      --category security \
      --content "$(cat org-security.md)"

In each project:

    # project/.kogral/config.ncl
    {
      inheritance = {
        base = "~/org/.kogral-shared",
        priority = 100,  # Project overrides shared
      },
    }

Developer in Project A:

    What are our error handling guidelines?

Claude (via `kb/get_guidelines` with inheritance):

    ## Rust Error Handling Guidelines

    ### Shared Guidelines (priority: 50)
    From ~/org/.kogral-shared:
    - Use thiserror for custom errors
    - Never unwrap() in production
    - Always propagate with ?

    ### Project Guidelines (priority: 100)
    From project .kogral/:
    - Additionally: log all errors with tracing
    - Use anyhow for application-level errors

    [Combined effective guidelines]

Result:

- Consistent practices across organization
- Projects can specialize where needed
- Single source of truth for shared knowledge

## Use Case 7: Bug Investigation

### Problem

Bug in production. Developer needs to:

- Understand relevant decisions
- Find related code patterns
- Check if similar issues were solved before

### Solution with KOGRAL

Developer:

    We have a race condition in the cache layer.
    What past decisions or patterns might be relevant?

Claude (semantic search):

    Found 4 relevant items:

    1. **Decision: Use DashMap for Concurrent Access** (accepted, 1 month ago)
       "Chose DashMap over RwLock<HashMap> to avoid race conditions in cache"

    2. **Pattern: Safe Concurrent Cache Access**
       "Always use lock-free data structures for high-contention caches"

    3. **Note: Cache Race Condition Fix** (from 2026-01-17 journal)
       "Fixed race in cache by switching to DashMap"

    4. **Guideline: Concurrency Safety**
       "Use atomic operations and lock-free structures where possible"

    It looks like we've seen this before! Check the journal entry from 2026-01-17.

Developer reads journal:

    kb show journal-2026-01-17

Finds:

- Exact same issue
- Solution: switch to DashMap
- Code example included
- Performance benchmarks

Applies fix in 10 minutes instead of 2 hours of debugging.

## Use Case 8: API Design Collaboration

### Problem

Team discussing new API design. Multiple approaches:

- REST vs GraphQL
- Versioning strategy
- Authentication method

Need to capture discussion and decision.

### Solution with KOGRAL

During design meeting (using Claude Code):

    We're designing the new API. Let me capture this discussion:

    Approaches discussed:
    1. REST with versioned endpoints (/v1/, /v2/)
    2. GraphQL with schema versioning
    3. gRPC for internal services

    Team consensus:
    - REST for public API (easier for partners)
    - GraphQL for internal admin tools (flexibility)
    - gRPC for service-to-service (performance)

    Create a decision record for each, linking them together.

Claude (creates 3 linked ADRs):

    Created 3 decisions:

    1. decision-rest-public-api
       Status: accepted
       Context: Public API needs broad compatibility
       Decision: REST with /v1/, /v2/ versioning

    2. decision-graphql-admin
       Status: accepted
       Context: Admin tools need flexible queries
       Decision: GraphQL with schema evolution

    3. decision-grpc-internal
       Status: accepted
       Context: Service-to-service needs performance
       Decision: gRPC with protobuf

    Linked:
      decision-rest-public-api --[relates_to]--> decision-graphql-admin
      decision-rest-public-api --[relates_to]--> decision-grpc-internal
      decision-graphql-admin --[relates_to]--> decision-grpc-internal

    All decisions documented and interconnected.

Two months later, someone asks "Why REST not GraphQL for public API?":

    kb show decision-rest-public-api

Full context preserved, including relationship to GraphQL decision.

## Summary

Knowledge Base excels at:

✅ Onboarding: Historical context instantly available ✅ Decision Making: ADRs preserve rationale ✅ Pattern Reuse: Solutions documented once, used forever ✅ Daily Tracking: Development journal with semantic search ✅ Code Review: Implementation rationale linked to decisions ✅ Cross-Project: Shared guidelines with project overrides ✅ Bug Investigation: Past solutions easily discovered ✅ Collaboration: Discussions captured and interconnected

Common Theme: Knowledge captured during work, queryable when needed, connected to related concepts.

## Next Steps

- Start simple: Quick Start Guide
- Integrate AI: MCP Quick Guide
- Advanced features: Configuration Reference

# Best Practices

# System Architecture

Comprehensive overview of the KOGRAL architecture.

## High-Level Architecture

The KOGRAL consists of three main layers:

1. User Interfaces: kb-cli (terminal), kb-mcp (AI integration), NuShell scripts (automation)
2. Core Library (kb-core): Rust library with graph engine, storage abstraction, embeddings, query engine
3. Storage Backends: Filesystem (git-friendly), SurrealDB (scalable), In-Memory (cache/testing)

## Component Details

### kb-cli (Command-Line Interface)

Purpose: Primary user interface for local knowledge management.

Commands (13 total):

- `init`: Initialize `.kogral/` directory
- `add`: Create nodes (note, decision, guideline, pattern, journal)
- `search`: Text and semantic search
- `link`: Create relationships between nodes
- `list`: List all nodes
- `show`: Display node details
- `delete`: Remove nodes
- `graph`: Visualize knowledge graph
- `sync`: Sync filesystem ↔ SurrealDB
- `serve`: Start MCP server
- `import`: Import from Logseq
- `export`: Export to Logseq/JSON
- `config`: Manage configuration

Technology: Rust + clap (derive API)

Features:

- Colored terminal output
- Interactive prompts
- Dry-run modes
- Validation before operations

### kb-mcp (MCP Server)

Purpose: AI integration via Model Context Protocol.

Protocol: JSON-RPC 2.0 over stdio

Components:

1. Tools (7):

   - `kogral/search`: Query knowledge base
   - `kb/add_note`: Create notes
   - `kb/add_decision`: Create ADRs
   - `kb/link`: Create relationships
   - `kb/get_guidelines`: Retrieve guidelines with inheritance
   - `kb/list_graphs`: List available graphs
   - `kb/export`: Export to formats

2. Resources (6 URIs):

   - `kogral://project/notes`
   - `kogral://project/decisions`
   - `kogral://project/guidelines`
   - `kogral://project/patterns`
   - `kogral://shared/guidelines`
   - `kogral://shared/patterns`

3. Prompts (2):

   - `kb/summarize_project`: Generate project summary
   - `kb/find_related`: Find related nodes

Integration: Claude Code via `~/.config/claude/config.json`

### NuShell Scripts

Purpose: Automation and maintenance tasks.

Scripts (6):

- `kb-sync.nu`: Filesystem ↔ SurrealDB sync
- `kb-backup.nu`: Archive knowledge base
- `kb-reindex.nu`: Rebuild embeddings
- `kb-import-logseq.nu`: Import from Logseq
- `kb-export-logseq.nu`: Export to Logseq
- `kb-stats.nu`: Graph statistics

Features:

- Colored output
- Dry-run modes
- Progress indicators
- Error handling

## Core Library (kb-core)

### Models

Graph:

    #![allow(unused)]
    fn main() {
    pub struct Graph {
        pub name: String,
        pub version: String,
        pub nodes: HashMap<String, Node>,  // ID → Node
        pub edges: Vec<Edge>,
        pub metadata: HashMap<String, Value>,
    }
    }

Node:

    #![allow(unused)]
    fn main() {
    pub struct Node {
        pub id: String,
        pub node_type: NodeType,
        pub title: String,
        pub content: String,
        pub tags: Vec<String>,
        pub status: NodeStatus,
        pub created: DateTime<Utc>,
        pub modified: DateTime<Utc>,
        // ... relationships, metadata
    }
    }

Edge:

    #![allow(unused)]
    fn main() {
    pub struct Edge {
        pub from: String,
        pub to: String,
        pub relation: EdgeType,
        pub strength: f32,
        pub created: DateTime<Utc>,
    }
    }

### Storage Trait

    #![allow(unused)]
    fn main() {
    #[async_trait]
    pub trait Storage: Send + Sync {
        async fn save_graph(&self, graph: &Graph) -> Result<()>;
        async fn load_graph(&self, name: &str) -> Result<Graph>;
        async fn delete_graph(&self, name: &str) -> Result<()>;
        async fn list_graphs(&self) -> Result<Vec<String>>;
    }
    }

Implementations:

1. `FilesystemStorage`: Git-friendly markdown files
2. `MemoryStorage`: In-memory with DashMap
3. `SurrealDbStorage`: Scalable graph database

### Embedding Provider Trait

    #![allow(unused)]
    fn main() {
    #[async_trait]
    pub trait EmbeddingProvider: Send + Sync {
        async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
        fn dimensions(&self) -> usize;
        fn model_name(&self) -> &str;
    }
    }

Implementations:

1. `FastEmbedProvider`: Local fastembed
2. `RigEmbeddingProvider`: OpenAI, Claude, Ollama (via rig-core)

### Parser

Input: Markdown file with YAML frontmatter

Output: `Node` struct

Features:

- YAML frontmatter extraction
- Markdown body parsing
- Wikilink detection (`[[linked-note]]`)
- Code reference parsing (`@file.rs:42`)

Example:

    ---
    id: note-123
    type: note
    title: My Note
    tags: [rust, async]
    ---

    # My Note

    Content with [[other-note]] and @src/main.rs:10

→

    #![allow(unused)]
    fn main() {
    Node {
        id: "note-123",
        node_type: NodeType::Note,
        title: "My Note",
        content: "Content with [[other-note]] and @src/main.rs:10",
        tags: vec!["rust", "async"],
        // ... parsed wikilinks, code refs
    }
    }

## Configuration System

### Nickel Schema

    # schemas/kb-config.ncl
    {
      KbConfig = {
        graph | GraphConfig,
        storage | StorageConfig,
        embeddings | EmbeddingConfig,
        templates | TemplateConfig,
        query | QueryConfig,
        mcp | McpConfig,
        sync | SyncConfig,
      },
    }

### Loading Process

    User writes: .kogral/config.ncl
        ↓ [nickel export --format json]
    JSON intermediate
        ↓ [serde_json::from_str]
    KbConfig struct (Rust)
        ↓
    Runtime behavior

Double Validation:

1. Nickel contracts: Type-safe, enum validation
2. Serde deserialization: Rust type checking

Benefits:

- Errors caught at export time
- Runtime guaranteed valid config
- Self-documenting schemas

## Storage Architecture

### Hybrid Strategy

Local Graph (per project):

- Storage: Filesystem (`.kogral/` directory)
- Format: Markdown + YAML frontmatter
- Version control: Git
- Scope: Project-specific knowledge

Shared Graph (organization):

- Storage: SurrealDB (or synced filesystem)
- Format: Same markdown (for compatibility)
- Version control: Optional
- Scope: Organization-wide guidelines

Sync:

    Filesystem (.kogral/)
        ↕ [bidirectional sync]
    SurrealDB (central)

### File Layout

    .kogral/
    ├── config.toml              # Graph metadata
    ├── notes/
    │   ├── async-patterns.md    # Individual note
    │   └── error-handling.md
    ├── decisions/
    │   ├── 0001-use-rust.md     # ADR format
    │   └── 0002-surrealdb.md
    ├── guidelines/
    │   ├── rust-errors.md       # Project guideline
    │   └── testing.md
    ├── patterns/
    │   └── repository.md
    └── journal/
        ├── 2026-01-17.md        # Daily journal
        └── 2026-01-18.md

## Query Engine

### Text Search

    #![allow(unused)]
    fn main() {
    let results = graph.nodes.values()
        .filter(|node| {
            node.title.contains(&query) ||
            node.content.contains(&query) ||
            node.tags.iter().any(|tag| tag.contains(&query))
        })
        .collect();
    }

### Semantic Search

    #![allow(unused)]
    fn main() {
    let query_embedding = embeddings.embed(vec![query]).await?;
    let mut scored: Vec<_> = graph.nodes.values()
        .filter_map(|node| {
            let node_embedding = node.embedding.as_ref()?;
            let similarity = cosine_similarity(&query_embedding[0], node_embedding);
            (similarity >= threshold).then_some((node, similarity))
        })
        .collect();
    scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
    }

### Cross-Graph Query

    #![allow(unused)]
    fn main() {
    // Query both project and shared graphs
    let project_results = project_graph.search(&query).await?;
    let shared_results = shared_graph.search(&query).await?;

    // Merge with deduplication
    let combined = merge_results(project_results, shared_results);
    }

## MCP Protocol Flow

    Claude Code                kb-mcp                    kb-core
        │                         │                         │
        ├─ JSON-RPC request ───→ │                         │
        │  kb/search              │                         │
        │  {"query": "rust"}      │                         │
        │                         ├─ search() ──────────→  │
        │                         │                         │
        │                         │                    Query engine
        │                         │                    Text + semantic
        │                         │                         │
        │                         │ ←──── results ─────────┤
        │                         │                         │
        │ ←─ JSON-RPC response ──┤                         │
        │  {"results": [...]}     │                         │

## Template System

Engine: Tera (Jinja2-like)

Templates:

1. Document Templates (6):

   - `note.md.tera`
   - `decision.md.tera`
   - `guideline.md.tera`
   - `pattern.md.tera`
   - `journal.md.tera`
   - `execution.md.tera`

2. Export Templates (4):

   - `logseq-page.md.tera`
   - `logseq-journal.md.tera`
   - `summary.md.tera`
   - `graph.json.tera`

Usage:

    #![allow(unused)]
    fn main() {
    let mut tera = Tera::new("templates/**/*.tera")?;
    let rendered = tera.render("note.md.tera", &context)?;
    }

## Error Handling

Strategy: `thiserror` for structured errors

    #![allow(unused)]
    fn main() {
    #[derive(Error, Debug)]
    pub enum KbError {
        #[error("Storage error: {0}")]
        Storage(String),

        #[error("Node not found: {0}")]
        NodeNotFound(String),

        #[error("Configuration error: {0}")]
        Config(String),

        #[error("Parse error: {0}")]
        Parse(String),

        #[error("Embedding error: {0}")]
        Embedding(String),
    }
    }

Propagation: `?` operator throughout

## Testing Strategy

Unit Tests: Per module (models, parser, storage)

Integration Tests: Full workflow (add → save → load → query)

Test Coverage:

- kb-core: 48 tests
- kb-mcp: 5 tests
- Total: 56 tests

Test Data: Fixtures in `tests/fixtures/`

## Performance Considerations

Node Lookup: O(1) via HashMap

Semantic Search: O(n) with early termination (threshold filter)

Storage:

- Filesystem: Lazy loading (load on demand)
- Memory: Full graph in RAM
- SurrealDB: Query optimization (indexes)

Embeddings:

- Cache embeddings in node metadata
- Batch processing (configurable batch size)
- Async generation (non-blocking)

## Security

No unsafe code: `#![forbid(unsafe_code)]`

Input validation:

- Nickel contracts validate config
- serde validates JSON
- Custom validation for user input

File operations:

- Path sanitization (no `../` traversal)
- Permissions checking
- Atomic writes (temp file + rename)

## Scalability

Small Projects (< 1000 nodes):

- Filesystem storage
- In-memory search
- Local embeddings (fastembed)

Medium Projects (1000-10,000 nodes):

- Filesystem + SurrealDB sync
- Semantic search with caching
- Cloud embeddings (OpenAI/Claude)

Large Organizations (> 10,000 nodes):

- SurrealDB primary
- Distributed embeddings
- Multi-graph federation

## Next Steps

- Graph Model Details: Graph Model
- Storage Deep Dive: Storage Architecture
- ADRs: Architectural Decisions
- Implementation: Development Guide

# Graph Model

# Config-Driven Architecture

The KOGRAL follows a config-driven architecture where all behavior is defined through Nickel configuration files rather than hardcoded in Rust.

## Philosophy

"Configuration, not code, defines behavior"

Instead of hardcoding storage backends, embedding providers, or query parameters, KB uses a layered configuration system that composes settings from multiple sources:

1. Schema contracts (type definitions)
2. Defaults (base values)
3. Mode overlays (dev/prod/test optimizations)
4. User customizations (project-specific overrides)

This approach provides:

- ✅ Type safety - Nickel contracts validate configuration before runtime
- ✅ Composability - Mix and match configurations for different environments
- ✅ Discoverability - Self-documenting schemas with inline documentation
- ✅ Hot-reload - Change behavior without recompiling Rust code
- ✅ Double validation - Nickel contracts + serde ensure correctness

## Configuration Composition Flow

The configuration system uses a four-layer composition pattern:

### Layer 1: Schema Contracts

Location: `schemas/kb/contracts.ncl`

Purpose: Define types and validation rules using Nickel contracts.

Example:

    {
      StorageType = [| 'filesystem, 'memory, 'surrealdb |],

      StorageConfig = {
        primary | StorageType
          | doc "Primary storage backend"
          | default = 'filesystem,

        secondary | SecondaryStorageConfig
          | doc "Optional secondary storage"
          | default = { enabled = false },
      },
    }

Benefits:

- Enum validation (only valid storage types accepted)
- Required vs optional fields
- Default values for optional fields
- Documentation attached to types

### Layer 2: Defaults

Location: `schemas/kb/defaults.ncl`

Purpose: Provide sensible base values for all configuration options.

Example:

    {
      base = {
        storage = {
          primary = 'filesystem,
          secondary = {
            enabled = false,
            type = 'surrealdb,
            url = "ws://localhost:8000",
          },
        },
        embeddings = {
          enabled = true,
          provider = 'fastembed,
          model = "BAAI/bge-small-en-v1.5",
          dimensions = 384,
        },
      } | contracts.KbConfig,
    }

Validated by: `contracts.KbConfig` contract ensures defaults are valid.

### Layer 3: Mode Overlays

Location: `schemas/kb/modes/{dev,prod,test}.ncl`

Purpose: Environment-specific optimizations and tuning.

#### Development Mode (`dev.ncl`)

Optimized for: Fast iteration, local development, debugging

    {
      storage = {
        primary = 'filesystem,
        secondary = { enabled = false },  # No database overhead
      },
      embeddings = {
        provider = 'fastembed,  # Local, no API costs
      },
      sync = {
        auto_index = false,  # Manual control
      },
    }

#### Production Mode (`prod.ncl`)

Optimized for: Performance, reliability, scalability

    {
      storage = {
        secondary = { enabled = true },  # SurrealDB for scale
      },
      embeddings = {
        provider = 'openai,  # High-quality cloud embeddings
        model = "text-embedding-3-small",
        dimensions = 1536,
      },
      sync = {
        auto_index = true,
        debounce_ms = 300,  # Fast response
      },
    }

#### Test Mode (`test.ncl`)

Optimized for: Fast tests, isolation, determinism

    {
      storage = {
        primary = 'memory,  # Ephemeral, no disk I/O
      },
      embeddings = {
        enabled = false,  # Disable for speed
      },
      sync = {
        auto_index = false,
        debounce_ms = 0,  # No delays in tests
      },
    }

### Layer 4: User Customizations

Location: `.kb-config/core/kb.ncl` or `.kb-config/platform/{dev,prod,test}.ncl`

Purpose: Project-specific or deployment-specific overrides.

Example (user project config):

    let mode = import "../../schemas/kb/modes/dev.ncl" in

    let user_custom = {
      graph = {
        name = "my-project",
      },
      embeddings = {
        provider = 'claude,  # Override to use Claude
        model = "claude-3-haiku-20240307",
      },
      query = {
        similarity_threshold = 0.7,  # Stricter threshold
      },
    } in

    helpers.compose_config defaults.base mode user_custom
      | contracts.KbConfig

## Composition Mechanism

The `helpers.ncl` module provides the composition function:

    {
      # Recursively merge with override precedence
      merge_with_override = fun base override => /* ... */,

      # Compose three layers
      compose_config = fun defaults mode_config user_custom =>
        let with_mode = merge_with_override defaults mode_config in
        merge_with_override with_mode user_custom,
    }

Merge behavior:

- Records are merged recursively
- Override values take precedence over base values
- Arrays are not merged, override replaces base
- Null in override keeps base value

Example merge:

    base = { storage = { primary = 'filesystem }, embeddings = { enabled = true } }
    override = { storage = { primary = 'memory } }
    # Result: { storage = { primary = 'memory }, embeddings = { enabled = true } }

## Export to JSON

Once composed, the Nickel configuration is exported to JSON for Rust consumption:

    nickel export --format json .kb-config/core/kb.ncl > .kb-config/targets/kb-core.json

Output (`.kb-config/targets/kb-core.json`):

    {
      "graph": {
        "name": "my-project",
        "version": "1.0.0"
      },
      "storage": {
        "primary": "memory",
        "secondary": {
          "enabled": false
        }
      },
      "embeddings": {
        "enabled": true,
        "provider": "claude",
        "model": "claude-3-haiku-20240307",
        "dimensions": 768
      }
    }

## Rust Integration

The Rust code deserializes the JSON into typed structs using serde:

    #![allow(unused)]
    fn main() {
    use serde::{Deserialize, Serialize};

    #[derive(Debug, Deserialize, Serialize)]
    pub struct KbConfig {
        pub graph: GraphConfig,
        pub storage: StorageConfig,
        pub embeddings: EmbeddingConfig,
        pub templates: TemplateConfig,
        pub query: QueryConfig,
        pub mcp: McpConfig,
        pub sync: SyncConfig,
    }

    impl KbConfig {
        pub fn from_file(path: &Path) -> Result<Self> {
            let json = std::fs::read_to_string(path)?;
            let config: KbConfig = serde_json::from_str(&json)?;
            Ok(config)
        }
    }
    }

Usage in kb-core:

    #![allow(unused)]
    fn main() {
    let config = KbConfig::from_file(".kb-config/targets/kb-core.json")?;

    // Config drives behavior
    let storage: Box<dyn Storage> = match config.storage.primary {
        StorageType::Filesystem => Box::new(FilesystemStorage::new(&config)?),
        StorageType::Memory => Box::new(MemoryStorage::new()),
        StorageType::SurrealDb => Box::new(SurrealDbStorage::new(&config).await?),
    };

    let embeddings: Box<dyn EmbeddingProvider> = match config.embeddings.provider {
        EmbeddingProviderType::FastEmbed => Box::new(FastEmbedProvider::new()?),
        EmbeddingProviderType::OpenAI => Box::new(RigEmbeddingProvider::openai(&config)?),
        EmbeddingProviderType::Claude => Box::new(RigEmbeddingProvider::claude(&config)?),
        EmbeddingProviderType::Ollama => Box::new(RigEmbeddingProvider::ollama(&config)?),
    };
    }

## Double Validation

Configuration is validated twice:

### 1. Nickel Contract Validation

At export time, Nickel validates:

- ✅ Types match contracts (e.g., `primary | StorageType`)
- ✅ Required fields are present
- ✅ Enums have valid values
- ✅ Nested structure is correct

Error example:

    error: contract broken by a value
      ┌─ .kb-config/core/kb.ncl:15:5
      │
    15│     primary = 'invalid,
      │     ^^^^^^^^^^^^^^^^^^^ applied to this expression
      │
      = This value is not in the enum ['filesystem, 'memory, 'surrealdb]

### 2. Serde Deserialization Validation

At runtime, serde validates:

- ✅ JSON structure matches Rust types
- ✅ Field names match (with rename support)
- ✅ Values can be converted to Rust types
- ✅ Required fields are not null

Error example:

    #![allow(unused)]
    fn main() {
    Error: missing field `graph` at line 1 column 123
    }

## Benefits of Config-Driven Architecture

### 1. Zero Hardcoding

Bad (hardcoded):

    #![allow(unused)]
    fn main() {
    // Hardcoded - requires recompilation to change
    let storage = FilesystemStorage::new("/fixed/path");
    let threshold = 0.6;
    }

Good (config-driven):

    #![allow(unused)]
    fn main() {
    // Config-driven - change via .ncl file
    let storage = create_storage(&config)?;
    let threshold = config.query.similarity_threshold;
    }

### 2. Environment Flexibility

Same codebase, different behavior:

    # Development
    nickel export .kb-config/platform/dev.ncl > targets/kb-core.json
    # → Filesystem storage, fastembed, no auto-sync

    # Production
    nickel export .kb-config/platform/prod.ncl > targets/kb-core.json
    # → SurrealDB enabled, OpenAI embeddings, auto-sync

    # Testing
    nickel export .kb-config/platform/test.ncl > targets/kb-core.json
    # → In-memory storage, no embeddings, isolated

### 3. Self-Documenting

Nickel contracts include inline documentation:

    StorageType = [| 'filesystem, 'memory, 'surrealdb |]
      | doc "Storage backend type: filesystem (git-tracked), memory (ephemeral), surrealdb (scalable)",

IDEs can show this documentation when editing `.ncl` files.

### 4. Type-Safe Evolution

When adding new features:

1. Update contract in `contracts.ncl`
2. Add default in `defaults.ncl`
3. Export validates existing configs
4. Rust compilation validates deserialization

Breaking changes are caught before runtime.

### 5. Testability

Different test scenarios without code changes:

    # test-semantic-search.ncl
    let test_config = defaults.base & {
      embeddings = { enabled = true, provider = 'fastembed },
      query = { similarity_threshold = 0.3 },
    } in test_config

    #![allow(unused)]
    fn main() {
    #[test]
    fn test_semantic_search() {
        let config = KbConfig::from_file("test-semantic-search.json")?;
        // Config drives test behavior
    }
    }

## Configuration Discovery

KB tools automatically discover configuration:

1. Check `.kb-config/targets/kb-core.json` (pre-exported)
2. Check `.kb-config/core/kb.ncl` (export on-demand)
3. Check environment variable `KB_CONFIG`
4. Fall back to embedded defaults

    #![allow(unused)]
    fn main() {
    impl KbConfig {
        pub fn discover() -> Result<Self> {
            if let Ok(config) = Self::from_file(".kb-config/targets/kb-core.json") {
                return Ok(config);
            }

            if Path::new(".kb-config/core/kb.ncl").exists() {
                // Export and load
                let output = Command::new("nickel")
                    .args(["export", "--format", "json", ".kb-config/core/kb.ncl"])
                    .output()?;
                return serde_json::from_slice(&output.stdout)?;
            }

            if let Ok(path) = std::env::var("KB_CONFIG") {
                return Self::from_file(&path);
            }

            Ok(Self::default())  // Embedded defaults
        }
    }
    }

## Integration with justfile

The `justfile` integrates configuration validation:

    # Validate all Nickel configs
    nickel-validate-all:
        @echo "Validating Nickel schemas..."
        nickel typecheck schemas/kb/contracts.ncl
        nickel typecheck schemas/kb/defaults.ncl
        nickel typecheck schemas/kb/helpers.ncl
        nickel typecheck schemas/kb/modes/dev.ncl
        nickel typecheck schemas/kb/modes/prod.ncl
        nickel typecheck schemas/kb/modes/test.ncl
        nickel typecheck .kb-config/core/kb.ncl

    # Export all platform configs
    nickel-export-all:
        @echo "Exporting platform configs to JSON..."
        @mkdir -p .kb-config/targets
        nickel export --format json .kb-config/platform/dev.ncl > .kb-config/targets/kb-dev.json
        nickel export --format json .kb-config/platform/prod.ncl > .kb-config/targets/kb-prod.json
        nickel export --format json .kb-config/platform/test.ncl > .kb-config/targets/kb-test.json
        @echo "Exported 3 configurations to .kb-config/targets/"

Usage:

    just nickel::validate-all  # Check configs are valid
    just nickel::export-all    # Generate JSON for all environments

## Best Practices

### 1. Never Hardcode

If it's behavior, it's config:

- Storage paths
- API endpoints
- Thresholds
- Timeouts
- Feature flags
- Provider selection

### 2. Use Modes for Environment Differences

Don't put environment-specific values in user config:

Bad:

    # user config with env-specific values
    {
      storage = {
        url = if env == "prod" then "prod-url" else "dev-url"  # Don't do this
      }
    }

Good:

    # modes/prod.ncl
    { storage = { url = "prod-url" } }

    # modes/dev.ncl
    { storage = { url = "dev-url" } }

    # user config is environment-agnostic
    { graph = { name = "my-project" } }

### 3. Document Complex Fields

Use Nickel's `doc` metadata:

    similarity_threshold | Number
      | doc "Minimum cosine similarity (0-1) for semantic search matches. Higher = stricter."
      | default = 0.6,

### 4. Validate Early

Run `nickel typecheck` in CI/CD before building Rust code.

### 5. Version Configs

Track `.ncl` files in git, ignore `.kb-config/targets/*.json` (generated).

## See Also

- Schema Reference: Configuration Schema
- User Guide: Configuration Guide
- ADR: Why Nickel vs TOML
- Examples: `.kb-config/core/kb.ncl`, `.kb-config/platform/*.ncl`

# Storage Architecture

# Logseq Blocks Support - Architecture Design

## Problem Statement

Logseq uses content blocks as the fundamental unit of information, not full documents. Each block can have:

- Properties: `#card`, `TODO`, `DONE`, custom properties
- Tags: Inline tags like `#flashcard`, `#important`
- References: Block references `((block-id))`, page references `[[page]]`
- Nesting: Outliner-style hierarchy (parent-child blocks)
- Metadata: Block-level properties (unlike page-level frontmatter)

Current KB limitation: Nodes only have `content: String` (flat markdown). Importing from Logseq loses block structure and properties.

Requirement: Support round-trip import/export with full block fidelity:

    Logseq Graph → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq Graph
         (blocks preserved)                    (blocks preserved)

## Use Cases

### 1. Flashcards (`#card`)

Logseq:

    - What is Rust's ownership model? #card
      - Rust uses ownership, borrowing, and lifetimes
      - Three rules: one owner, many borrows XOR one mutable

KB needs to preserve:

- Block with `#card` property
- Nested answer blocks
- Ability to query all cards

### 2. Task Tracking (`TODO`/`DONE`)

Logseq:

    - TODO Implement block parser #rust
      - DONE Research block structure
      - TODO Write parser tests

KB needs to preserve:

- Task status per block
- Hierarchical task breakdown
- Tags on tasks

### 3. Block References

Logseq:

    - Core concept: ((block-uuid-123))
      - See also: [[Related Page]]

KB needs to preserve:

- Block-to-block links (not just page-to-page)
- UUID references

### 4. Block Properties

Logseq:

    - This is a block with properties
      property1:: value1
      property2:: value2

KB needs to preserve:

- Custom key-value properties per block
- Property inheritance/override

## Design Options

### Option A: Blocks as First-Class Data Structure

Add `blocks` field to Node:

    #![allow(unused)]
    fn main() {
    pub struct Node {
        // ... existing fields ...
        pub content: String,              // Backward compat: flat markdown
        pub blocks: Option<Vec<Block>>,   // NEW: Structured blocks
    }

    pub struct Block {
        pub id: String,                   // UUID or auto-generated
        pub content: String,              // Block text
        pub properties: BlockProperties,  // Tags, status, custom props
        pub children: Vec<Block>,         // Nested blocks
        pub created: DateTime<Utc>,
        pub modified: DateTime<Utc>,
    }

    pub struct BlockProperties {
        pub tags: Vec<String>,            // #card, #important
        pub status: Option<TaskStatus>,   // TODO, DONE, WAITING
        pub custom: HashMap<String, String>, // property:: value
    }

    pub enum TaskStatus {
        Todo,
        Doing,
        Done,
        Waiting,
        Cancelled,
    }
    }

Pros:

- ✅ Type-safe, explicit structure
- ✅ Queryable (find all #card blocks)
- ✅ Preserves hierarchy
- ✅ Supports block-level operations

Cons:

- ❌ Adds complexity to Node
- ❌ Dual representation (content + blocks)
- ❌ Requires migration of existing data

### Option B: Parser-Only Approach

Keep `content: String`, parse blocks on-demand:

    #![allow(unused)]
    fn main() {
    pub struct BlockParser;

    impl BlockParser {
        // Parse markdown content into block structure
        fn parse(content: &str) -> Vec<Block>;

        // Serialize blocks back to markdown
        fn serialize(blocks: &[Block]) -> String;
    }

    // Usage
    let blocks = BlockParser::parse(&node.content);
    let filtered = blocks.iter().filter(|b| b.properties.tags.contains("card"));
    }

Pros:

- ✅ No schema changes
- ✅ Backward compatible
- ✅ Simple storage (still just String)

Cons:

- ❌ Parse overhead on every access
- ❌ Can't query blocks in database (SurrealDB)
- ❌ Harder to index/search blocks

### Option C: Hybrid Approach (RECOMMENDED)

Combine both: structured storage + lazy parsing:

    #![allow(unused)]
    fn main() {
    pub struct Node {
        // ... existing fields ...
        pub content: String,              // Source of truth (markdown)

        #[serde(skip_serializing_if = "Option::is_none")]
        pub blocks: Option<Vec<Block>>,   // Cached structure (parsed)
    }

    impl Node {
        // Parse blocks from content if not already cached
        pub fn get_blocks(&mut self) -> &Vec<Block> {
            if self.blocks.is_none() {
                self.blocks = Some(BlockParser::parse(&self.content));
            }
            self.blocks.as_ref().unwrap()
        }

        // Update content from blocks (when blocks modified)
        pub fn sync_blocks_to_content(&mut self) {
            if let Some(ref blocks) = self.blocks {
                self.content = BlockParser::serialize(blocks);
            }
        }
    }
    }

Storage Strategy:

1. Filesystem - Store as markdown (Logseq compatible):

       - Block 1 #card
         - Nested block
       - Block 2 TODO

2. SurrealDB - Store both:

       DEFINE TABLE block SCHEMAFULL;
       DEFINE FIELD node_id ON block TYPE record(node);
       DEFINE FIELD block_id ON block TYPE string;
       DEFINE FIELD content ON block TYPE string;
       DEFINE FIELD properties ON block TYPE object;
       DEFINE FIELD parent_id ON block TYPE option<string>;

       -- Index for queries
       DEFINE INDEX block_tags ON block COLUMNS properties.tags;
       DEFINE INDEX block_status ON block COLUMNS properties.status;

Pros:

- ✅ Best of both worlds
- ✅ Filesystem stays Logseq-compatible
- ✅ SurrealDB can query blocks
- ✅ Lazy parsing (only when needed)
- ✅ Backward compatible

Cons:

- ⚠️ Need to keep content/blocks in sync
- ⚠️ More complex implementation

## Recommended Implementation

Phase 1: Data Model

    #![allow(unused)]
    fn main() {
    // crates/kb-core/src/models/block.rs

    use chrono::{DateTime, Utc};
    use serde::{Deserialize, Serialize};
    use std::collections::HashMap;

    /// A content block (Logseq-style)
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct Block {
        /// Unique block identifier (UUID)
        pub id: String,

        /// Block content (markdown text, excluding nested blocks)
        pub content: String,

        /// Block properties (tags, status, custom)
        pub properties: BlockProperties,

        /// Child blocks (nested hierarchy)
        #[serde(default)]
        pub children: Vec<Block>,

        /// Creation timestamp
        pub created: DateTime<Utc>,

        /// Last modification timestamp
        pub modified: DateTime<Utc>,

        /// Parent block ID (if nested)
        #[serde(skip_serializing_if = "Option::is_none")]
        pub parent_id: Option<String>,
    }

    /// Block-level properties
    #[derive(Debug, Clone, Default, Serialize, Deserialize)]
    pub struct BlockProperties {
        /// Tags (e.g., #card, #important)
        #[serde(default)]
        pub tags: Vec<String>,

        /// Task status (TODO, DONE, etc.)
        #[serde(skip_serializing_if = "Option::is_none")]
        pub status: Option<TaskStatus>,

        /// Custom properties (property:: value)
        #[serde(default)]
        pub custom: HashMap<String, String>,

        /// Block references ((uuid))
        #[serde(default)]
        pub block_refs: Vec<String>,

        /// Page references ([[page]])
        #[serde(default)]
        pub page_refs: Vec<String>,
    }

    /// Task status for TODO blocks
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
    #[serde(rename_all = "UPPERCASE")]
    pub enum TaskStatus {
        Todo,
        Doing,
        Done,
        Later,
        Now,
        Waiting,
        Cancelled,
    }

    impl Block {
        /// Create a new block with content
        pub fn new(content: String) -> Self {
            use uuid::Uuid;
            Self {
                id: Uuid::new_v4().to_string(),
                content,
                properties: BlockProperties::default(),
                children: Vec::new(),
                created: Utc::now(),
                modified: Utc::now(),
                parent_id: None,
            }
        }

        /// Add a child block
        pub fn add_child(&mut self, mut child: Block) {
            child.parent_id = Some(self.id.clone());
            self.children.push(child);
            self.modified = Utc::now();
        }

        /// Add a tag to this block
        pub fn add_tag(&mut self, tag: String) {
            if !self.properties.tags.contains(&tag) {
                self.properties.tags.push(tag);
                self.modified = Utc::now();
            }
        }

        /// Set task status
        pub fn set_status(&mut self, status: TaskStatus) {
            self.properties.status = Some(status);
            self.modified = Utc::now();
        }

        /// Get all blocks (self + descendants) as flat list
        pub fn flatten(&self) -> Vec<&Block> {
            let mut result = vec![self];
            for child in &self.children {
                result.extend(child.flatten());
            }
            result
        }

        /// Find block by ID in tree
        pub fn find(&self, id: &str) -> Option<&Block> {
            if self.id == id {
                return Some(self);
            }
            for child in &self.children {
                if let Some(found) = child.find(id) {
                    return Some(found);
                }
            }
            None
        }
    }
    }

Phase 2: Update Node Model

    #![allow(unused)]
    fn main() {
    // crates/kb-core/src/models.rs (modifications)

    use crate::models::block::Block;

    pub struct Node {
        // ... existing fields ...
        pub content: String,

        /// Structured blocks (optional, parsed from content)
        #[serde(skip_serializing_if = "Option::is_none")]
        pub blocks: Option<Vec<Block>>,
    }

    impl Node {
        /// Get blocks, parsing from content if needed
        pub fn get_blocks(&mut self) -> Result<&Vec<Block>> {
            if self.blocks.is_none() {
                self.blocks = Some(crate::parser::BlockParser::parse(&self.content)?);
            }
            Ok(self.blocks.as_ref().unwrap())
        }

        /// Update content from blocks
        pub fn sync_blocks_to_content(&mut self) {
            if let Some(ref blocks) = self.blocks {
                self.content = crate::parser::BlockParser::serialize(blocks);
            }
        }

        /// Find all blocks with a specific tag
        pub fn find_blocks_by_tag(&mut self, tag: &str) -> Result<Vec<&Block>> {
            let blocks = self.get_blocks()?;
            let mut result = Vec::new();
            for block in blocks {
                for b in block.flatten() {
                    if b.properties.tags.iter().any(|t| t == tag) {
                        result.push(b);
                    }
                }
            }
            Ok(result)
        }

        /// Find all TODO blocks
        pub fn find_todos(&mut self) -> Result<Vec<&Block>> {
            let blocks = self.get_blocks()?;
            let mut result = Vec::new();
            for block in blocks {
                for b in block.flatten() {
                    if matches!(b.properties.status, Some(TaskStatus::Todo)) {
                        result.push(b);
                    }
                }
            }
            Ok(result)
        }
    }
    }

Phase 3: Block Parser

    #![allow(unused)]
    fn main() {
    // crates/kb-core/src/parser/block_parser.rs

    use crate::models::block::{Block, BlockProperties, TaskStatus};
    use regex::Regex;

    pub struct BlockParser;

    impl BlockParser {
        /// Parse markdown content into block structure
        ///
        /// Handles:
        /// - Outliner format (- prefix with indentation)
        /// - Tags (#card, #important)
        /// - Task status (TODO, DONE)
        /// - Properties (property:: value)
        /// - Block references (((uuid)))
        /// - Page references ([[page]])
        pub fn parse(content: &str) -> Result<Vec<Block>> {
            let mut blocks = Vec::new();
            let mut stack: Vec<(usize, Block)> = Vec::new(); // (indent_level, block)

            for line in content.lines() {
                // Detect indentation level
                let indent = count_indent(line);
                let trimmed = line.trim_start();

                // Skip empty lines
                if trimmed.is_empty() {
                    continue;
                }

                // Parse block line
                if let Some(block_content) = trimmed.strip_prefix("- ") {
                    let mut block = Self::parse_block_line(block_content)?;

                    // Pop stack until we find parent level
                    while let Some((level, _)) = stack.last() {
                        if *level < indent {
                            break;
                        }
                        stack.pop();
                    }

                    // Add as child to parent or as root
                    if let Some((_, parent)) = stack.last_mut() {
                        parent.add_child(block.clone());
                    } else {
                        blocks.push(block.clone());
                    }

                    stack.push((indent, block));
                }
            }

            Ok(blocks)
        }

        /// Parse a single block line (after "- " prefix)
        fn parse_block_line(line: &str) -> Result<Block> {
            let mut block = Block::new(String::new());
            let mut properties = BlockProperties::default();

            // Extract task status (TODO, DONE, etc.)
            let (status, remaining) = Self::extract_task_status(line);
            properties.status = status;

            // Extract tags (#card, #important)
            let (tags, remaining) = Self::extract_tags(remaining);
            properties.tags = tags;

            // Extract properties (property:: value)
            let (custom_props, remaining) = Self::extract_properties(remaining);
            properties.custom = custom_props;

            // Extract block references (((uuid)))
            let (block_refs, remaining) = Self::extract_block_refs(remaining);
            properties.block_refs = block_refs;

            // Extract page references ([[page]])
            let (page_refs, content) = Self::extract_page_refs(remaining);
            properties.page_refs = page_refs;

            block.content = content.trim().to_string();
            block.properties = properties;

            Ok(block)
        }

        /// Serialize blocks back to markdown
        pub fn serialize(blocks: &[Block]) -> String {
            let mut result = String::new();
            for block in blocks {
                Self::serialize_block(&mut result, block, 0);
            }
            result
        }

        fn serialize_block(output: &mut String, block: &Block, indent: usize) {
            // Write indent
            for _ in 0..indent {
                output.push_str("  ");
            }

            // Write prefix
            output.push_str("- ");

            // Write task status
            if let Some(status) = block.properties.status {
                output.push_str(&format!("{:?} ", status).to_uppercase());
            }

            // Write content
            output.push_str(&block.content);

            // Write tags
            for tag in &block.properties.tags {
                output.push_str(&format!(" #{}", tag));
            }

            // Write properties
            if !block.properties.custom.is_empty() {
                output.push('\n');
                for (key, value) in &block.properties.custom {
                    for _ in 0..=indent {
                        output.push_str("  ");
                    }
                    output.push_str(&format!("{}:: {}\n", key, value));
                }
            }

            output.push('\n');

            // Write children recursively
            for child in &block.children {
                Self::serialize_block(output, child, indent + 1);
            }
        }

        // Helper methods for extraction
        fn extract_task_status(line: &str) -> (Option<TaskStatus>, &str) {
            let line = line.trim_start();
            if let Some(rest) = line.strip_prefix("TODO ") {
                (Some(TaskStatus::Todo), rest)
            } else if let Some(rest) = line.strip_prefix("DONE ") {
                (Some(TaskStatus::Done), rest)
            } else if let Some(rest) = line.strip_prefix("DOING ") {
                (Some(TaskStatus::Doing), rest)
            } else if let Some(rest) = line.strip_prefix("LATER ") {
                (Some(TaskStatus::Later), rest)
            } else if let Some(rest) = line.strip_prefix("NOW ") {
                (Some(TaskStatus::Now), rest)
            } else if let Some(rest) = line.strip_prefix("WAITING ") {
                (Some(TaskStatus::Waiting), rest)
            } else if let Some(rest) = line.strip_prefix("CANCELLED ") {
                (Some(TaskStatus::Cancelled), rest)
            } else {
                (None, line)
            }
        }

        fn extract_tags(line: &str) -> (Vec<String>, String) {
            let tag_regex = Regex::new(r"#(\w+)").unwrap();
            let mut tags = Vec::new();
            let mut result = line.to_string();

            for cap in tag_regex.captures_iter(line) {
                if let Some(tag) = cap.get(1) {
                    tags.push(tag.as_str().to_string());
                    result = result.replace(&format!("#{}", tag.as_str()), "");
                }
            }

            (tags, result.trim().to_string())
        }

        fn extract_properties(line: &str) -> (HashMap<String, String>, String) {
            let prop_regex = Regex::new(r"(\w+)::\s*([^\n]+)").unwrap();
            let mut props = HashMap::new();
            let mut result = line.to_string();

            for cap in prop_regex.captures_iter(line) {
                if let (Some(key), Some(value)) = (cap.get(1), cap.get(2)) {
                    props.insert(key.as_str().to_string(), value.as_str().trim().to_string());
                    result = result.replace(&cap[0], "");
                }
            }

            (props, result.trim().to_string())
        }

        fn extract_block_refs(line: &str) -> (Vec<String>, String) {
            let ref_regex = Regex::new(r"\(\(([^)]+)\)\)").unwrap();
            let mut refs = Vec::new();
            let mut result = line.to_string();

            for cap in ref_regex.captures_iter(line) {
                if let Some(uuid) = cap.get(1) {
                    refs.push(uuid.as_str().to_string());
                    result = result.replace(&cap[0], "");
                }
            }

            (refs, result.trim().to_string())
        }

        fn extract_page_refs(line: &str) -> (Vec<String>, String) {
            let page_regex = Regex::new(r"\[\[([^\]]+)\]\]").unwrap();
            let mut pages = Vec::new();
            let result = line.to_string();

            for cap in page_regex.captures_iter(line) {
                if let Some(page) = cap.get(1) {
                    pages.push(page.as_str().to_string());
                    // Keep [[page]] in content for now (backward compat)
                }
            }

            (pages, result)
        }
    }

    fn count_indent(line: &str) -> usize {
        line.chars().take_while(|c| c.is_whitespace()).count() / 2
    }
    }

Phase 4: Logseq Import/Export

    #![allow(unused)]
    fn main() {
    // crates/kb-core/src/logseq.rs

    use crate::models::{Node, NodeType};
    use crate::models::block::Block;
    use crate::parser::BlockParser;

    pub struct LogseqImporter;

    impl LogseqImporter {
        /// Import a Logseq page (markdown file) as a Node
        pub fn import_page(path: &Path) -> Result<Node> {
            let content = std::fs::read_to_string(path)?;

            // Extract frontmatter if present
            let (frontmatter, body) = Self::split_frontmatter(&content);

            // Parse blocks from body
            let blocks = BlockParser::parse(&body)?;

            // Create node with blocks
            let mut node = Node::new(NodeType::Note, Self::extract_title(path));
            node.content = body;
            node.blocks = Some(blocks);

            // Apply frontmatter properties
            if let Some(fm) = frontmatter {
                Self::apply_frontmatter(&mut node, &fm)?;
            }

            Ok(node)
        }

        fn split_frontmatter(content: &str) -> (Option<String>, String) {
            if content.starts_with("---\n") {
                if let Some(end) = content[4..].find("\n---\n") {
                    let frontmatter = content[4..4 + end].to_string();
                    let body = content[4 + end + 5..].to_string();
                    return (Some(frontmatter), body);
                }
            }
            (None, content.to_string())
        }

        fn extract_title(path: &Path) -> String {
            path.file_stem()
                .and_then(|s| s.to_str())
                .unwrap_or("Untitled")
                .to_string()
        }

        fn apply_frontmatter(node: &mut Node, frontmatter: &str) -> Result<()> {
            // Parse YAML frontmatter and apply to node
            // ... implementation ...
            Ok(())
        }
    }

    pub struct LogseqExporter;

    impl LogseqExporter {
        /// Export a Node to Logseq page format
        pub fn export_page(node: &Node, path: &Path) -> Result<()> {
            let mut output = String::new();

            // Generate frontmatter
            output.push_str("---\n");
            output.push_str(&Self::generate_frontmatter(node)?);
            output.push_str("---\n\n");

            // Serialize blocks or use content
            if let Some(ref blocks) = node.blocks {
                output.push_str(&BlockParser::serialize(blocks));
            } else {
                output.push_str(&node.content);
            }

            std::fs::write(path, output)?;
            Ok(())
        }

        fn generate_frontmatter(node: &Node) -> Result<String> {
            let mut fm = String::new();
            fm.push_str(&format!("title: {}\n", node.title));
            fm.push_str(&format!("tags: {}\n", node.tags.join(", ")));
            // ... more frontmatter fields ...
            Ok(fm)
        }
    }
    }

## Query API Extensions

    #![allow(unused)]
    fn main() {
    // New methods in Graph or Query module

    impl Graph {
        /// Find all blocks with a specific tag across all nodes
        pub fn find_blocks_by_tag(&mut self, tag: &str) -> Vec<(&Node, &Block)> {
            let mut results = Vec::new();
            for node in self.nodes.values_mut() {
                if let Ok(blocks) = node.find_blocks_by_tag(tag) {
                    for block in blocks {
                        results.push((node as &Node, block));
                    }
                }
            }
            results
        }

        /// Find all flashcards (#card blocks)
        pub fn find_flashcards(&mut self) -> Vec<(&Node, &Block)> {
            self.find_blocks_by_tag("card")
        }

        /// Find all TODO items across knowledge base
        pub fn find_all_todos(&mut self) -> Vec<(&Node, &Block)> {
            let mut results = Vec::new();
            for node in self.nodes.values_mut() {
                if let Ok(todos) = node.find_todos() {
                    for block in todos {
                        results.push((node as &Node, block));
                    }
                }
            }
            results
        }
    }
    }

## MCP Tool Extensions

    {
      "name": "kogral/find_blocks",
      "description": "Find blocks by tag, status, or properties",
      "inputSchema": {
        "type": "object",
        "properties": {
          "tag": { "type": "string", "description": "Filter by tag (e.g., 'card')" },
          "status": { "type": "string", "enum": ["TODO", "DONE", "DOING"] },
          "property": { "type": "string", "description": "Custom property key" },
          "value": { "type": "string", "description": "Property value to match" }
        }
      }
    }

## Configuration

    # schemas/kb/contracts.ncl (additions)

    BlockConfig = {
      enabled | Bool
        | doc "Enable block-level parsing and storage"
        | default = true,

      preserve_hierarchy | Bool
        | doc "Preserve block nesting on import/export"
        | default = true,

      parse_on_load | Bool
        | doc "Automatically parse blocks when loading nodes"
        | default = false,  # Lazy parsing by default

      supported_statuses | Array String
        | doc "Supported task statuses"
        | default = ["TODO", "DONE", "DOING", "LATER", "NOW", "WAITING", "CANCELLED"],
    }

    KbConfig = {
      # ... existing fields ...

      blocks | BlockConfig
        | doc "Block-level features configuration"
        | default = {},
    }

## Migration Path

Phase 1: Add Block models (no behavior change) Phase 2: Add BlockParser (opt-in via config) Phase 3: Update Logseq import/export Phase 4: Add block queries to CLI/MCP Phase 5: SurrealDB block indexing

Backward Compatibility:

- Existing nodes without `blocks` field work as before
- `content` remains source of truth
- `blocks` is optional cache/structure
- Config flag `blocks.enabled` to opt-in

## Testing Strategy

    #![allow(unused)]
    fn main() {
    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn test_parse_simple_block() {
            let content = "- This is a block #card";
            let blocks = BlockParser::parse(content).unwrap();

            assert_eq!(blocks.len(), 1);
            assert_eq!(blocks[0].content, "This is a block");
            assert_eq!(blocks[0].properties.tags, vec!["card"]);
        }

        #[test]
        fn test_parse_nested_blocks() {
            let content = r#"
    - Parent block
      - Child block 1
      - Child block 2
        - Grandchild
    "#;
            let blocks = BlockParser::parse(content).unwrap();

            assert_eq!(blocks.len(), 1);
            assert_eq!(blocks[0].children.len(), 2);
            assert_eq!(blocks[0].children[1].children.len(), 1);
        }

        #[test]
        fn test_parse_todo() {
            let content = "- TODO Implement feature #rust";
            let blocks = BlockParser::parse(content).unwrap();

            assert_eq!(blocks[0].properties.status, Some(TaskStatus::Todo));
            assert_eq!(blocks[0].content, "Implement feature");
            assert_eq!(blocks[0].properties.tags, vec!["rust"]);
        }

        #[test]
        fn test_roundtrip() {
            let original = r#"- Block 1 #card
      - Nested
    - TODO Block 2
      priority:: high
    "#;
            let blocks = BlockParser::parse(original).unwrap();
            let serialized = BlockParser::serialize(&blocks);
            let reparsed = BlockParser::parse(&serialized).unwrap();

            assert_eq!(blocks.len(), reparsed.len());
            assert_eq!(blocks[0].properties, reparsed[0].properties);
        }
    }
    }

## Summary

Recommended Approach: Hybrid (Option C)

- Add `Block` struct with properties, hierarchy
- Extend `Node` with optional `blocks: Option<Vec<Block>>`
- Implement bidirectional parser (markdown ↔ blocks)
- Preserve `content` as source of truth (backward compat)
- Enable block queries in CLI/MCP
- Support round-trip Logseq import/export

Benefits:

- ✅ Full Logseq compatibility
- ✅ Queryable blocks (find #card, TODO, etc.)
- ✅ Backward compatible
- ✅ Extensible (custom properties)
- ✅ Type-safe structure

Trade-offs:

- ⚠️ Added complexity
- ⚠️ Need to sync content ↔ blocks
- ⚠️ More storage for SurrealDB backend

Next Steps:

1. Review and approve design
2. Implement Phase 1 (Block models)
3. Implement Phase 2 (BlockParser)
4. Update Logseq import/export
5. Add block queries to MCP/CLI

# ADR-001: Nickel vs TOML for Configuration

Status: Accepted

Date: 2026-01-17

Deciders: Architecture Team

Context: Configuration Strategy for Knowledge Base System

## Context

The KOGRAL requires a flexible, type-safe configuration format that supports:

1. Complex nested structures (graph settings, storage configs, embedding providers)
2. Type validation (prevent runtime errors from config mistakes)
3. Composition and inheritance (shared configs, environment-specific overrides)
4. Documentation (self-documenting schemas)
5. Validation before runtime (catch errors early)

We evaluated two primary options:

### Option 1: TOML (Traditional Config Format)

Pros:

- Widely adopted in Rust ecosystem (`Cargo.toml`)
- Simple, human-readable syntax
- Native `serde` support
- IDE support (syntax highlighting, completion)

Cons:

- No type system (validation only at runtime)
- Limited composition (no imports, no functions)
- No schema validation (errors discovered during execution)
- Verbose for complex nested structures
- No documentation in config files

Example TOML:

    [graph]
    name = "my-project"
    version = "1.0.0"

    [storage]
    primary = "filesystem"  # String, not validated as enum

    [storage.secondary]
    enabled = true
    type = "surrealdb"  # Typo would fail at runtime
    url = "ws://localhost:8000"

    [embeddings]
    enabled = true
    provider = "openai"  # No validation of valid providers
    model = "text-embedding-3-small"

Problems:

- Typos in enum values (`"surrealdb"` vs `"surealdb"`) fail at runtime
- No validation that `provider = "openai"` requires `api_key_env`
- No documentation of valid options
- No way to compose configs (e.g., base config + environment override)

### Option 2: Nickel (Functional Configuration Language)

Pros:

- Type system with contracts (validate before runtime)
- Composition via imports and merging
- Documentation in schemas (self-documenting)
- Validation at export time (catch errors early)
- Functions for conditional logic
- Default values in schema definitions

Cons:

- Less familiar to Rust developers
- Requires separate `nickel` CLI tool
- Smaller ecosystem
- Steeper learning curve

Example Nickel:

    # schemas/kb-config.ncl
    {
      KbConfig = {
        graph | GraphConfig,
        storage | StorageConfig,
        embeddings | EmbeddingConfig,
      },

      StorageConfig = {
        primary | [| 'filesystem, 'memory |],  # Enum validated at export

        secondary | {
          enabled | Bool,
          type | [| 'surrealdb, 'sqlite |],  # Typos caught immediately
          url | String,
        } | optional,
      },

      EmbeddingConfig = {
        enabled | Bool,
        provider | [| 'openai, 'claude, 'fastembed |],  # Valid providers enforced
        model | String,
        api_key_env | String | doc "Environment variable for API key",
      },
    }

Benefits:

- Typos in enum values caught at `nickel export` time
- Schema enforces required fields based on provider
- Documentation embedded in schema
- Config can be composed: `import "base.ncl" & { /* overrides */ }`

## Decision

We will use Nickel for configuration.

Implementation:

1. Define schemas in `schemas/*.ncl` with type contracts
2. Users write configs in `.kogral/config.ncl`
3. Export to JSON via CLI: `nickel export --format json config.ncl`
4. Load JSON in Rust via `serde_json` into typed structs

Pattern (double validation):

    Nickel Config (.ncl)
        ↓ [nickel export]
    JSON (validated by Nickel contracts)
        ↓ [serde_json::from_str]
    Rust Struct (validated by serde)
        ↓
    Runtime (guaranteed valid config)

Bridge Code (`kb-core/src/config/nickel.rs`):

    #![allow(unused)]
    fn main() {
    pub fn load_config<P: AsRef<Path>>(path: P) -> Result<KbConfig> {
        // Export Nickel to JSON
        let json = export_nickel_to_json(path)?;

        // Deserialize to Rust struct
        let config: KbConfig = serde_json::from_str(&json)?;

        Ok(config)
    }

    fn export_nickel_to_json<P: AsRef<Path>>(path: P) -> Result<String> {
        let output = Command::new("nickel")
            .arg("export")
            .arg("--format").arg("json")
            .arg(path.as_ref())
            .output()?;

        Ok(String::from_utf8(output.stdout)?)
    }
    }

## Consequences

### Positive

✅ Type Safety: Config errors caught before runtime

- Invalid enum values fail at export: `'filesystm` → error
- Missing required fields detected: no `graph.name` → error
- Type mismatches prevented: `enabled = "yes"` → error (expects Bool)

✅ Self-Documenting: Schemas serve as documentation

- `| doc "Environment variable for API key"` describes fields
- Enum options visible in schema: `[| 'openai, 'claude, 'fastembed |]`
- Default values explicit: `| default = 'filesystem`

✅ Composition: Config reuse and overrides

    # base.ncl
    { graph = { version = "1.0.0" } }

    # project.ncl
    import "base.ncl" & { graph = { name = "my-project" } }

✅ Validation Before Deployment: Catch errors in CI

    # CI pipeline
    nickel typecheck config.ncl
    nickel export --format json config.ncl > /dev/null

✅ Conditional Logic: Environment-specific configs

    let is_prod = std.string.is_match "prod" (std.env.get "ENV") in
    {
      embeddings = {
        provider = if is_prod then 'openai else 'fastembed,
      },
    }

### Negative

❌ Learning Curve: Team must learn Nickel syntax

- Mitigation: Provide comprehensive examples in `config/` directory
- Mitigation: Document common patterns in `docs/config/`

❌ Tool Dependency: Requires `nickel` CLI installed

- Mitigation: Document installation in setup guide
- Mitigation: Check `nickel` availability in `kb init` command

❌ IDE Support: Limited compared to TOML

- Mitigation: Use LSP (nickel-lang-lsp) for VSCode/Neovim
- Mitigation: Syntax highlighting available for major editors

❌ Ecosystem Size: Smaller than TOML

- Mitigation: Nickel actively developed by Tweag
- Mitigation: Stable language specification (v1.0+)

### Neutral

⚪ Two-Stage Loading: Nickel → JSON → Rust

- Not a performance concern (config loaded once at startup)
- Adds resilience (double validation)
- Allows runtime config inspection (read JSON directly)

## Alternatives Considered

### JSON Schema

Rejected: Not ergonomic for humans to write

- No comments
- Verbose syntax (`{"key": "value"}` vs `key = value`)
- JSON Schema separate from config (duplication)

### YAML

Rejected: No type system, ambiguous parsing

- Boolean confusion: `yes`/`no`/`on`/`off`/`true`/`false`
- Indentation-sensitive (error-prone)
- No validation without external tools

### Dhall

Rejected: More complex than needed

- Turing-incomplete by design (limits use cases)
- Smaller ecosystem than Nickel
- Steeper learning curve

### KCL (KusionStack Configuration Language)

Rejected: Kubernetes-focused, less general-purpose

- Designed for K8s manifests
- Less mature than Nickel for general config

## Implementation Timeline

1. ✅ Define base schemas (`schemas/kb-config.ncl`)
2. ✅ Implement Nickel loader (`kb-core/src/config/nickel.rs`)
3. ✅ Create example configs (`config/defaults.ncl`, `config/production.ncl`)
4. ✅ Document Nickel usage (`docs/config/nickel-schemas.md`)
5. ⏳ Add LSP recommendations to setup guide
6. ⏳ Create Nickel → TOML migration tool (for existing users)

## Monitoring

Success Criteria:

- Config errors caught at export time (not runtime)
- Users can compose configs for different environments
- Team comfortable with Nickel syntax within 2 weeks

Metrics:

- Number of config validation errors caught before runtime
- Time to diagnose config issues (should decrease)
- User feedback on config complexity

## References

- Nickel Language
- Nickel User Manual
- platform-config pattern (reference implementation)
- TOML Specification

## Revision History

Date | Author | Change
--- | --- | ---
2026-01-17 | Architecture Team | Initial decision

Next ADR: ADR-002: FastEmbed via AI Providers

# ADR-002: FastEmbed via AI Providers for Embeddings

Status: Accepted

Date: 2026-01-17

Deciders: Architecture Team

Context: Embedding Strategy for Semantic Search

## Context

The KOGRAL requires embedding generation for semantic search capabilities. Embeddings convert text into numerical vectors that capture semantic meaning, enabling "find concepts" rather than just "find keywords".

Requirements:

1. Local-First Option: Must work offline without external API dependencies
2. Production Scalability: Support cloud AI providers for large-scale deployments
3. Multiple Providers: Flexibility to choose based on cost, quality, privacy
4. Cost-Effective Development: Free local embeddings for development and testing
5. Quality: Good enough embeddings for finding related concepts

Options Evaluated:

### Option 1: Only Local Embeddings (fastembed)

Pros:

- No API costs
- Works offline
- Privacy-preserving (no data leaves machine)
- Fast (local GPU acceleration possible)

Cons:

- Limited model quality compared to cloud providers
- Resource-intensive (requires download ~100MB models)
- Single provider lock-in (fastembed library)

Example:

    #![allow(unused)]
    fn main() {
    use fastembed::{TextEmbedding, InitOptions};

    let model = TextEmbedding::try_new(InitOptions {
        model_name: "BAAI/bge-small-en-v1.5",
        ..Default::default()
    })?;

    let embeddings = model.embed(vec!["Hello world"], None)?;
    // Output: Vec<Vec<f32>> with 384 dimensions
    }

### Option 2: Only Cloud AI Providers (OpenAI, Claude, etc.)

Pros:

- State-of-the-art embedding quality
- No local resource usage
- Latest models available
- Scalable to millions of documents

Cons:

- Requires API keys (cost per embedding)
- Network dependency (no offline mode)
- Privacy concerns (data sent to third parties)
- Vendor lock-in risk

Example:

    #![allow(unused)]
    fn main() {
    use rig::providers::openai;

    let client = openai::Client::new("sk-...");
    let embeddings = client.embeddings("text-embedding-3-small")
        .embed_documents(vec!["Hello world"]).await?;
    // Output: Vec<Vec<f32>> with 1536 dimensions
    }

### Option 3: Hybrid Strategy (fastembed + AI providers via rig-core)

Pros:

- ✅ Best of both worlds: local dev, cloud production
- ✅ User choice: privacy-first or quality-first
- ✅ Cost flexibility: free for small projects, paid for scale
- ✅ Unified interface via `rig-core` library
- ✅ Easy provider switching (config-driven)

Cons:

- ❌ More complex implementation (multiple providers)
- ❌ Dimension mismatch between providers (384 vs 1536)
- ❌ Additional dependencies (`rig-core`, `fastembed`)

## Decision

We will use a hybrid strategy: fastembed (local) + AI providers (via rig-core).

Implementation:

1. Default: `fastembed` with `BAAI/bge-small-en-v1.5` (384 dimensions)
2. Optional: OpenAI, Claude, Ollama via `rig-core` (configurable)
3. Interface: `EmbeddingProvider` trait abstracts provider details
4. Config-Driven: Provider selection via Nickel configuration

Architecture:

    #![allow(unused)]
    fn main() {
    #[async_trait]
    pub trait EmbeddingProvider: Send + Sync {
        async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
        fn dimensions(&self) -> usize;
        fn model_name(&self) -> &str;
    }

    // Local implementation
    pub struct FastEmbedProvider {
        model: TextEmbedding,
    }

    impl FastEmbedProvider {
        pub fn new(model_name: &str) -> Result<Self> {
            let model = TextEmbedding::try_new(InitOptions {
                model_name: model_name.into(),
                ..Default::default()
            })?;
            Ok(Self { model })
        }
    }

    #[async_trait]
    impl EmbeddingProvider for FastEmbedProvider {
        async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
            Ok(self.model.embed(texts, None)?)
        }

        fn dimensions(&self) -> usize { 384 }
        fn model_name(&self) -> &str { "BAAI/bge-small-en-v1.5" }
    }

    // Cloud provider implementation (via rig-core)
    pub struct RigEmbeddingProvider {
        client: rig::Client,
        model: String,
        dimensions: usize,
    }

    #[async_trait]
    impl EmbeddingProvider for RigEmbeddingProvider {
        async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
            let embeddings = self.client
                .embeddings(&self.model)
                .embed_documents(texts)
                .await?;
            Ok(embeddings)
        }

        fn dimensions(&self) -> usize { self.dimensions }
        fn model_name(&self) -> &str { &self.model }
    }
    }

Configuration (Nickel):

    # Local development (default)
    {
      embeddings = {
        enabled = true,
        provider = 'fastembed,
        model = "BAAI/bge-small-en-v1.5",
        dimensions = 384,
      },
    }

    # Production with OpenAI
    {
      embeddings = {
        enabled = true,
        provider = 'openai,
        model = "text-embedding-3-small",
        dimensions = 1536,
        api_key_env = "OPENAI_API_KEY",
      },
    }

    # Self-hosted with Ollama
    {
      embeddings = {
        enabled = true,
        provider = 'ollama,
        model = "nomic-embed-text",
        dimensions = 768,
      },
    }

Provider Selection (`kb-core/src/embeddings/mod.rs`):

    #![allow(unused)]
    fn main() {
    pub fn create_provider(config: &EmbeddingConfig) -> Result<Box<dyn EmbeddingProvider>> {
        match config.provider {
            EmbeddingProviderType::FastEmbed => {
                Ok(Box::new(FastEmbedProvider::new(&config.model)?))
            }
            EmbeddingProviderType::OpenAI => {
                let api_key = std::env::var(&config.api_key_env)?;
                Ok(Box::new(RigEmbeddingProvider::new_openai(api_key, &config.model)?))
            }
            EmbeddingProviderType::Claude => {
                let api_key = std::env::var(&config.api_key_env)?;
                Ok(Box::new(RigEmbeddingProvider::new_claude(api_key, &config.model)?))
            }
            EmbeddingProviderType::Ollama => {
                Ok(Box::new(RigEmbeddingProvider::new_ollama(&config.model)?))
            }
        }
    }
    }

## Consequences

### Positive

✅ Development Flexibility:

- Developers can use `fastembed` without API keys
- Fast feedback loop (local embeddings, no network calls)
- Works offline (train trips, flights)

✅ Production Quality:

- Production deployments can use OpenAI/Claude for better quality
- Latest embedding models available
- Scalable to millions of documents

✅ Privacy Control:

- Privacy-sensitive projects use local embeddings
- Public projects can use cloud providers
- User choice via configuration

✅ Cost Optimization:

- Small projects: free (fastembed)
- Large projects: pay for quality (cloud providers)
- Hybrid: important docs via cloud, bulk via local

✅ Unified Interface:

- `EmbeddingProvider` trait abstracts provider details
- Query code doesn't know/care about provider
- Easy to add new providers

### Negative

❌ Dimension Mismatch:

- fastembed: 384 dimensions
- OpenAI: 1536 dimensions
- Cannot mix in same index

Mitigation:

- Store provider + dimensions in node metadata
- Rebuild index when changing providers
- Document dimension constraints

❌ Model Download:

- First use of fastembed downloads ~100MB model
- Slow initial startup

Mitigation:

- Pre-download in Docker images
- Document model download in setup guide
- Cache models in `~/.cache/fastembed`

❌ Complex Configuration:

- Multiple provider options may confuse users

Mitigation:

- Sane default (fastembed)
- Clear examples for each provider
- Validation errors explain misconfigurations

### Neutral

⚪ Dependency Trade-off:

- `fastembed` adds ~5MB to binary
- `rig-core` adds ~2MB
- Total: ~7MB overhead

Not a concern for CLI/MCP server use case.

## Provider Comparison

Provider | Dimensions | Quality | Cost | Privacy | Offline
--- | --- | --- | --- | --- | ---
fastembed | 384 | Good | Free | ✅ Local | ✅ Yes
OpenAI | 1536 | Excellent | $0.0001/1K | ❌ Cloud | ❌ No
Claude | 1024 | Excellent | $0.00025/1K | ❌ Cloud | ❌ No
Ollama | 768 | Very Good | Free | ✅ Local | ✅ Yes

Recommendation by Use Case:

- Development: fastembed (fast, free, offline)
- Small Teams: fastembed or Ollama (privacy, no costs)
- Enterprise: OpenAI or Claude (best quality, scalable)
- Self-Hosted: Ollama (good quality, local control)

## Implementation Timeline

1. ✅ Define `EmbeddingProvider` trait
2. ✅ Implement FastEmbedProvider (stub, feature-gated)
3. ✅ Implement RigEmbeddingProvider (stub, feature-gated)
4. ⏳ Complete FastEmbed integration with model download
5. ⏳ Complete rig-core integration (OpenAI, Claude, Ollama)
6. ⏳ Add query engine with similarity search
7. ⏳ Document provider selection and trade-offs

## Monitoring

Success Criteria:

- Users can switch providers via config change
- Local embeddings work without API keys
- Production deployments use cloud providers successfully
- Query quality acceptable for both local and cloud embeddings

Metrics:

- Embedding generation latency (local vs cloud)
- Query accuracy (precision@10 for semantic search)
- API costs (cloud providers)
- User satisfaction (feedback on search quality)

## References

- fastembed Documentation
- rig-core Documentation
- OpenAI Embeddings API
- BAAI/bge Models
- Ollama Embeddings

## Revision History

Date | Author | Change
--- | --- | ---
2026-01-17 | Architecture Team | Initial decision

Previous ADR: ADR-001: Nickel vs TOML Next ADR: ADR-003: Hybrid Storage Strategy

# ADR-003: Hybrid Storage Strategy

Status: Accepted

Date: 2026-01-17

Deciders: Architecture Team

Context: Storage Backend Strategy for Knowledge Base

## Context

The KOGRAL needs to store knowledge graphs with these requirements:

1. Git-Friendly: Knowledge should version alongside code
2. Scalable: Support small projects (10s of nodes) to large organizations (10,000+ nodes)
3. Queryable: Efficient graph queries and relationship traversal
4. Offline-Capable: Work without network access
5. Collaborative: Support shared organizational knowledge
6. Cost-Effective: Free for small projects, reasonable cost at scale

Constraints:

- Developers want to edit knowledge in text editors
- Organizations want centralized guideline management
- Git workflows essential for code-adjacent knowledge
- Large graphs need database performance

### Option 1: Filesystem Only

Approach: Store everything as markdown files

Pros:

- ✅ Git-native (perfect for versioning)
- ✅ Text editor friendly
- ✅ No dependencies
- ✅ Works offline
- ✅ Free

Cons:

- ❌ Poor performance for large graphs (100 0+ nodes)
- ❌ No efficient graph queries
- ❌ Difficult to share across projects
- ❌ Manual sync for collaboration

Scalability: Good for < 100 nodes, poor beyond

### Option 2: Database Only (SurrealDB)

Approach: Store all knowledge in SurrealDB graph database

Pros:

- ✅ Excellent query performance
- ✅ Native graph relationships
- ✅ Scalable to millions of nodes
- ✅ Centralized for collaboration

Cons:

- ❌ Not git-trackable
- ❌ Requires running database server
- ❌ Can't edit with text editor
- ❌ Network dependency
- ❌ Infrastructure cost

Scalability: Excellent, but loses developer workflow benefits

### Option 3: Hybrid (Filesystem + SurrealDB)

Approach: Filesystem for local project knowledge, SurrealDB for shared organizational knowledge

Pros:

- ✅ Git-friendly for project knowledge
- ✅ Text editor friendly
- ✅ Scalable for shared knowledge
- ✅ Works offline (local graph)
- ✅ Collaborative (shared graph)
- ✅ Cost-effective (DB only for shared)

Cons:

- ❌ More complex implementation
- ❌ Sync mechanism needed
- ❌ Two storage systems to manage

Scalability: Excellent - best of both worlds

## Decision

We will use a hybrid storage strategy: Filesystem (local) + SurrealDB (shared).

Architecture:

    ┌─────────────────────────────────────────────────────────────┐
    │                    Project A (.kogral/)                          │
    │  Storage: Filesystem (git-tracked)                          │
    │  Scope: Project-specific notes, decisions, patterns         │
    │  Access: Local only                                         │
    └──────────────────┬──────────────────────────────────────────┘
                       │
                       │ [inherits]
                       ↓
    ┌─────────────────────────────────────────────────────────────┐
    │           Shared KB (SurrealDB or synced filesystem)         │
    │  Storage: SurrealDB (scalable) or filesystem (synced)       │
    │  Scope: Organization-wide guidelines, patterns              │
    │  Access: All projects                                       │
    └─────────────────────────────────────────────────────────────┘

Implementation:

    # Project config
    {
      storage = {
        primary = 'filesystem,  # Local project knowledge
        secondary = {
          enabled = true,
          type = 'surrealdb,   # Shared knowledge
          url = "ws://kb-central.company.com:8000",
          namespace = "organization",
          database = "shared-kb",
        },
      },

      inheritance = {
        base = "surrealdb://organization/shared-kb",  # Inherit from shared
        priority = 100,  # Project overrides shared
      },
    }

Sync Strategy:

    .kogral/ (Filesystem)
        ↓ [on save]
    Watch for changes
        ↓ [debounced]
    Sync to SurrealDB
        ↓
    Shared graph updated
        ↓ [on query]
    Merge local + shared results

## Consequences

### Positive

✅ Developer Workflow Preserved:

    # Local knowledge workflow (unchanged)
    vim .kogral/notes/my-note.md
    git add .kogral/notes/my-note.md
    git commit -m "Add implementation note"
    git push

✅ Git Integration:

- Project knowledge versioned with code
- Branches include relevant knowledge
- Merges resolve knowledge conflicts
- PR reviews include knowledge changes

✅ Offline Development:

- Full functionality without network
- Shared guidelines cached locally
- Sync when reconnected

✅ Scalability:

- Projects: filesystem (100s of nodes, fine performance)
- Organization: SurrealDB (10,000+ nodes, excellent performance)

✅ Collaboration:

- Shared guidelines accessible to all projects
- Updates to shared knowledge propagate automatically
- Consistent practices across organization

✅ Cost-Effective:

- Small projects: free (filesystem only)
- Organizations: SurrealDB for shared only (not all project knowledge)

✅ Gradual Adoption:

- Start with filesystem only
- Add SurrealDB when needed
- Feature-gated (`--features surrealdb`)

### Negative

❌ Complexity:

- Two storage implementations
- Sync mechanism required
- Conflict resolution needed

Mitigation:

- Storage trait abstracts differences
- Sync is optional (can disable)
- Conflicts rare (guidelines change infrequently)

❌ Sync Latency:

- Changes to shared KB not instant in all projects

Mitigation:

- Acceptable latency (guidelines don't change rapidly)
- Manual sync command available (`kb sync`)
- Auto-sync on query (fetch latest)

❌ Infrastructure Requirement:

- SurrealDB server needed for shared KB

Mitigation:

- Optional (can use synced filesystem instead)
- Docker Compose for easy setup
- Managed SurrealDB Cloud option

### Neutral

⚪ Storage Trait Implementation:

    #![allow(unused)]
    fn main() {
    #[async_trait]
    pub trait Storage {
        async fn save_graph(&self, graph: &Graph) -> Result<()>;
        async fn load_graph(&self, name: &str) -> Result<Graph>;
        async fn list_graphs(&self) -> Result<Vec<String>>;
    }

    // Three implementations
    impl Storage for FilesystemStorage { /* ... */ }
    impl Storage for SurrealDbStorage { /* ... */ }
    impl Storage for MemoryStorage { /* ... */ }
    }

Abstraction makes multi-backend manageable.

## Use Cases

### Small Project (Solo Developer)

Config:

    { storage = { primary = 'filesystem } }

Behavior:

- All knowledge in `.kogral/` directory
- Git-tracked with code
- No database required
- Works offline

### Medium Project (Team)

Config:

    {
      storage = {
        primary = 'filesystem,
        secondary = {
          enabled = true,
          type = 'surrealdb,
          url = "ws://team-kb.local:8000",
        },
      },
    }

Behavior:

- Project knowledge in `.kogral/` (git-tracked)
- Shared team patterns in SurrealDB
- Automatic sync
- Offline fallback to cached

### Large Organization

Config:

    {
      storage = {
        primary = 'filesystem,
        secondary = {
          enabled = true,
          type = 'surrealdb,
          url = "ws://kb.company.com:8000",
          namespace = "engineering",
          database = "shared-kb",
        },
      },

      inheritance = {
        base = "surrealdb://engineering/shared-kb",
        guidelines = [
          "surrealdb://engineering/rust-guidelines",
          "surrealdb://engineering/security-policies",
        ],
      },
    }

Behavior:

- Project-specific in `.kogral/`
- Organization guidelines in SurrealDB
- Security policies enforced
- Automatic guideline updates

## Sync Mechanism

### Filesystem → SurrealDB

Trigger: File changes detected (via `notify` crate)

Process:

1. Parse changed markdown file
2. Convert to Node struct
3. Upsert to SurrealDB
4. Update relationships

Debouncing: 500ms (configurable)

### SurrealDB → Filesystem

Trigger: Query for shared knowledge

Process:

1. Query SurrealDB for shared nodes
2. Cache locally (in-memory or filesystem)
3. Merge with local results
4. Return combined

Caching: TTL-based (5 minutes default)

### Conflict Resolution

Strategy: Last-write-wins with version tracking

Example:

    Project A: Updates shared guideline (v1 → v2)
    Project B: Has cached v1

    On Project B query:
      - Detects v2 available
      - Fetches v2
      - Updates cache
      - Uses v2 going forward

## Alternatives Considered

### Git Submodules for Shared Knowledge

Rejected: Cumbersome workflow

- Requires manual submodule update
- Merge conflicts in shared submodule
- Not discoverable (need to know submodule exists)

### Syncthing for Filesystem Sync

Rejected: Not designed for this use case

- No query optimization
- No relationship indexes
- Sync conflicts difficult to resolve

### PostgreSQL with JSON

Rejected: Not a graph database

- Poor graph query performance
- Relationship traversal requires complex SQL joins
- No native graph features

## Migration Path

### Phase 1: Filesystem Only (Current)

- All storage via filesystem
- Git-tracked
- No database required

### Phase 2: Optional SurrealDB

- Add SurrealDB support (feature-gated)
- Manual sync command
- Shared KB opt-in

### Phase 3: Automatic Sync

- File watching
- Auto-sync on changes
- Background sync

### Phase 4: Multi-Tenant SurrealDB

- Organization namespaces
- Access control
- Audit logs

## Monitoring

Success Criteria:

- Developers don't notice hybrid complexity
- Sync completes < 1 second for typical changes
- Shared guidelines accessible in < 100ms
- Zero data loss in sync

Metrics:

- Sync latency (P50, P95, P99)
- Cache hit rate (shared knowledge)
- Conflict rate (expect < 0.1%)
- User satisfaction

## References

- SurrealDB Documentation
- Storage Trait Implementation
- FilesystemStorage
- SurrealDbStorage
- Sync Mechanism

## Revision History

Date | Author | Change
--- | --- | ---
2026-01-17 | Architecture Team | Initial decision

Previous ADR: ADR-002: FastEmbed via AI Providers Next ADR: ADR-004: Logseq Compatibility

# ADR-004: Logseq Blocks Support

## Status

Proposed (Design phase)

## Context

Logseq uses content blocks as the fundamental unit of information, not full documents. KB currently treats `Node.content` as flat markdown string, which loses block-level features on import/export:

Lost features:

- Block properties (`#card`, `TODO`, custom properties)
- Block hierarchy (outliner nesting)
- Block references (`((block-uuid))`)
- Block-level queries (find all flashcards, TODOs)

User requirement: Round-trip Logseq import/export with full fidelity:

    Logseq → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq
      (blocks preserved at every step)

## Decision

Implement Hybrid Block Support (structured + markdown):

### 1. Add Block Data Structure

    #![allow(unused)]
    fn main() {
    pub struct Block {
        pub id: String,                   // UUID
        pub content: String,              // Block text
        pub properties: BlockProperties,  // Tags, status, custom
        pub children: Vec<Block>,         // Nested blocks
        // ... timestamps ...
    }

    pub struct BlockProperties {
        pub tags: Vec<String>,            // #card, #important
        pub status: Option<TaskStatus>,   // TODO, DONE, etc.
        pub custom: HashMap<String, String>, // property:: value
        pub block_refs: Vec<String>,      // ((uuid))
        pub page_refs: Vec<String>,       // [[page]]
    }
    }

### 2. Extend Node Model

    #![allow(unused)]
    fn main() {
    pub struct Node {
        // ... existing fields ...
        pub content: String,              // Source of truth (markdown)
        pub blocks: Option<Vec<Block>>,   // Cached structure (optional)
    }
    }

### 3. Bidirectional Parser

- Parse: Markdown → `Vec<Block>` (lazy, on-demand)
- Serialize: `Vec<Block>` → Markdown (for export)

### 4. Storage Strategy

Filesystem (git-friendly, Logseq-compatible):

    - Block 1 #card
      - Nested answer
    - TODO Block 2
      priority:: high

SurrealDB (queryable):

    DEFINE TABLE block;
    DEFINE FIELD node_id ON block TYPE record(node);
    DEFINE FIELD block_id ON block TYPE string;
    DEFINE FIELD properties ON block TYPE object;
    DEFINE INDEX block_tags ON block COLUMNS properties.tags;

### 5. Query Extensions

    #![allow(unused)]
    fn main() {
    // Find all flashcards
    graph.find_blocks_by_tag("card")

    // Find all TODOs
    graph.find_all_todos()

    // Find blocks with custom property
    node.find_blocks_by_property("priority", "high")
    }

## Consequences

### Positive

✅ Full Logseq Compatibility - Import/export preserves all block features ✅ Queryable Blocks - Find #card, TODO, custom properties across KOGRAL ✅ Backward Compatible - Existing nodes without blocks still work ✅ Type-Safe - Structured data instead of regex parsing everywhere ✅ Extensible - Custom block properties supported ✅ Hierarchy Preserved - Nested blocks maintain parent-child relationships

### Negative

⚠️ Added Complexity - New data structures, parser, sync logic ⚠️ Dual Representation - Must keep `content` and `blocks` in sync ⚠️ Storage Overhead - SurrealDB stores both markdown and structure ⚠️ Migration Required - Existing data needs parsing to populate blocks

### Neutral

⚙️ Lazy Parsing - Blocks parsed on-demand (not stored by default) ⚙️ Opt-In - Config flag `blocks.enabled` to activate features ⚙️ Gradual Adoption - Can implement in phases

## Implementation Phases

Phase 1: Foundation (No behavior change)

- Add `Block` struct to `models/block.rs`
- Add optional `blocks` field to `Node`
- Add config: `blocks.enabled = false` (default off)

Phase 2: Parser

- Implement `BlockParser::parse()` (markdown → blocks)
- Implement `BlockParser::serialize()` (blocks → markdown)
- Add `Node::get_blocks()` method (lazy parsing)

Phase 3: Logseq Integration

- Update `LogseqImporter` to parse blocks
- Update `LogseqExporter` to serialize blocks
- Test round-trip (Logseq → KB → Logseq)

Phase 4: Query API

- Add `Graph::find_blocks_by_tag()`
- Add `Graph::find_all_todos()`
- Add `Node::find_blocks_by_property()`

Phase 5: MCP/CLI Integration

- Add `kb/find_blocks` MCP tool
- Add `kb find-cards` CLI command
- Add `kb find-todos` CLI command

Phase 6: SurrealDB Backend

- Create `block` table schema
- Index on tags, status, properties
- Store blocks alongside nodes

## Alternatives Considered

### Alternative 1: Blocks as First-Class Nodes

Convert each Logseq block to a separate KOGRAL Node.

Rejected: Too granular, explosion of nodes, loses document context.

### Alternative 2: Parser-Only (No Storage)

Keep `content: String`, parse blocks on every access.

Rejected: Can't query blocks in database, parse overhead, can't index.

### Alternative 3: Metadata Field

Store blocks in `metadata: HashMap<String, Value>`.

Rejected: Not type-safe, harder to query, no schema validation.

## References

- Logseq Block Format
- Full Design Document
- Implementation Tracking

## Notes

Backward Compatibility Strategy:

- `content` remains source of truth
- `blocks` is optional enhancement
- Old code works unchanged
- New features opt-in via config

Migration Path:

- Existing users: blocks disabled by default
- New users: blocks enabled, parsed on import
- Manual: `kb reindex --parse-blocks` to populate

Decision Date: 2026-01-17 Approvers: TBD Review Date: After Phase 2 implementation

# ADR-005: MCP Protocol for AI Integration

# Prerequisites

# Installation Methods

# Project Initialization

# Environment Configuration

# Verification

# Configuration Overview

# Nickel Schemas

# Graph Settings

# Inheritance

# Examples

# Storage Backends

# Filesystem Storage

# SurrealDB Storage

# In-Memory Storage

# Sync Strategies

# Embeddings Overview

# Provider Configuration

# FastEmbed Local

# OpenAI Integration

# Claude Integration

# Ollama Integration

# Semantic Search

# Template System

# Document Templates

# Export Templates

# Customization

# CLI Overview

# Commands Reference

# Workflows

# NuShell Scripts

# MCP Quick Guide

Fast-track guide to integrating KOGRAL with Claude Code via the Model Context Protocol (MCP).

## What is MCP?

MCP (Model Context Protocol) is a protocol that allows Claude Code to interact with external tools and data sources. The kogral-mcp server exposes your KOGRAL knowledge base to Claude Code for AI-assisted knowledge management.

## Quick Setup (5 Minutes)

### Step 1: Build MCP Server

    # Build kogral-mcp server
    cargo build --package kb-mcp --release

    # Verify binary
    ls -lh target/release/kb-mcp

### Step 2: Configure Claude Code

Add to `~/.config/claude/config.json`:

    {
      "mcpServers": {
        "kogral-mcp": {
          "command": "/path/to/knowledge-base/target/release/kb-mcp",
          "args": ["serve"],
          "env": {
            "KOGRAL_DIR": "/path/to/your/project/.kogral"
          }
        }
      }
    }

Replace `/path/to/knowledge-base` and `/path/to/your/project/.kogral` with your actual paths.

### Step 3: Initialize KOGRAL

    # Navigate to your project
    cd /path/to/your/project

    # Initialize .kb directory
    kb init

### Step 4: Start Claude Code

    # Start Claude Code (will auto-connect to kb-mcp)
    claude-code

### Step 5: Test Connection

In Claude Code, try:

    Search for "error handling"

Claude will use the `kogral/search` tool to query your knowledge base.

## Essential Commands

### Search Knowledge Base

Natural language:

    Find notes about Rust error handling

Claude translates to:

    {
      "tool": "kogral/search",
      "params": {
        "query": "error handling",
        "type": "note",
        "semantic": true
      }
    }

### Add Note

Natural language:

    Add a note about async Rust patterns with tags rust, async, patterns

Claude translates to:

    {
      "tool": "kogral/add_note",
      "params": {
        "title": "Async Rust Patterns",
        "content": "...",
        "tags": ["rust", "async", "patterns"]
      }
    }

### Add Decision

Natural language:

    Document decision to use SurrealDB for storage

Claude translates to:

    {
      "tool": "kogral/add_decision",
      "params": {
        "title": "Use SurrealDB for Storage",
        "context": "Need scalable graph database",
        "decision": "Adopt SurrealDB as primary storage backend",
        "consequences": ["Better query performance", "Additional infrastructure"]
      }
    }

### Get Guidelines

Natural language:

    What are our Rust error handling guidelines?

Claude translates to:

    {
      "tool": "kogral/get_guidelines",
      "params": {
        "language": "rust",
        "category": "error-handling"
      }
    }

### Link Concepts

Natural language:

    Link note-async-patterns to decision-use-tokio as implements

Claude translates to:

    {
      "tool": "kogral/link",
      "params": {
        "from": "note-async-patterns",
        "to": "decision-use-tokio",
        "relation": "implements"
      }
    }

## Common Workflows

### Capture Meeting Notes

You:

    We just decided in our meeting to migrate from REST to GraphQL.
    Context: current REST API is too chatty, requires multiple round trips.
    Consequences: better client performance, but team needs to learn GraphQL.
    Create a decision record.

Claude (uses `kb/add_decision`):

    Created decision: decision-migrate-to-graphql

    Status: proposed
    Context: Current REST API requires multiple round trips (chatty)
    Decision: Migrate from REST to GraphQL API
    Consequences:
      - ✅ Better client performance
      - ❌ Team learning curve for GraphQL

### Find Related Knowledge

You:

    I'm working on error handling. What related notes and patterns do we have?

Claude (uses `kogral/search` with semantic=true):

    Found 5 related items:

    1. **Error Handling with thiserror** (pattern)
       Tags: rust, error-handling
       Created: 2026-01-15

    2. **Result Type Best Practices** (guideline)
       Tags: rust, error-handling, best-practices
       Created: 2026-01-10

    3. **Decision: Use anyhow for Application Errors** (decision)
       Status: accepted
       Created: 2026-01-08

    4. **Custom Error Types** (note)
       Tags: rust, error-handling
       Created: 2026-01-05

    5. **Error Propagation Patterns** (pattern)
       Tags: rust, error-handling
       Created: 2026-01-03

    Would you like me to link your current work to any of these?

### Document Implementation

You:

    I just implemented the user authentication module following our security guidelines.
    Add a note and link it to our security guideline.

Claude (uses `kb/add_note` + `kb/link`):

    Created note: note-user-auth-implementation
    Tags: security, authentication, implementation

    Linked:
      note-user-auth-implementation --[implements]--> guideline-security-practices

    Done! Your implementation is now documented and connected to our guidelines.

### Review Project Knowledge

You:

    Summarize our project knowledge: what decisions have we made, what patterns are we using?

Claude (uses `kb/summarize_project` prompt):

    # Project Knowledge Summary

    **Total Nodes**: 42

    ## Recent Decisions (5)
    - Use SurrealDB for Storage (accepted, 2 days ago)
    - Adopt MCP for AI Integration (accepted, 5 days ago)
    - Nickel for Configuration (accepted, 1 week ago)

    ## Active Patterns (8)
    - Error Handling with thiserror
    - Repository Pattern for DB
    - Builder Pattern for Complex Objects
    - ...

    ## Top Tags
    1. rust (23 nodes)
    2. architecture (12 nodes)
    3. error-handling (8 nodes)
    4. patterns (7 nodes)

    ## Guideline Coverage
    - Rust: 5 guidelines
    - Testing: 3 guidelines
    - Security: 2 guidelines

## Advanced Usage

### Conditional Queries

You:

    Find all decisions made in the last week that are still proposed (not accepted)

Claude (combines `kogral/search` with filtering):

    Found 2 proposed decisions from last week:

    1. **Migrate to GraphQL** (proposed)
       Created: 3 days ago
       Context: REST API is too chatty...

    2. **Add Redis Caching** (proposed)
       Created: 5 days ago
       Context: Database queries are slow...

    Would you like to review these and update their status?

### Cross-Reference Check

You:

    Before I implement X, check if we have any decisions or guidelines about it

Claude (uses `kogral/search` + `kogral/get_guidelines`):

    Checking existing knowledge about X...

    **Decisions**: None found
    **Guidelines**: 1 relevant guideline found
      - guideline-module-structure: "Keep modules focused and small"

    **Related Notes**: 2 notes mention similar topics
      - note-module-design-patterns
      - note-separation-of-concerns

    Recommendation: Your implementation of X looks aligned with our guidelines.
    Consider linking it to guideline-module-structure when done.

### Export for Sharing

You:

    Export our architectural decisions to Logseq so I can review them visually

Claude (uses `kb/export`):

    Exporting to Logseq format...

    ✓ Exported 12 decisions to /Users/akasha/logseq-export/
      - 12 decision records
      - Preserved wikilinks
      - Added Logseq properties

    You can now open /Users/akasha/logseq-export/ in Logseq to visualize the decision graph.

## Tips and Tricks

### 1. Use Natural Language

Don't worry about exact tool syntax. Claude understands:

❌ Don't say: "Use kb/search with query='rust' and type='pattern'" ✅ Do say: "Find Rust patterns in KOGRAL"

### 2. Be Specific with Tags

When adding notes, use consistent tags:

✅ Good: `tags: rust, error-handling, pattern` ❌ Bad: `tags: Rust, ErrorHandling, patterns`

### 3. Link as You Go

After creating notes, ask Claude to link them:

    Link this note to our error handling guideline as 'implements'

### 4. Review Regularly

Ask Claude for summaries:

    What have we documented this week?

### 5. Use Semantic Search

For conceptual queries:

    Find anything related to "making code maintainable"

Not just keyword "maintainable", but concepts like refactoring, clean code, patterns, etc.

## Troubleshooting

### "MCP server not responding"

    # Check kb-mcp is built
    ls target/release/kb-mcp

    # Test manually
    echo '{"jsonrpc":"2.0","id":1,"method":"kogral/search","params":{"query":"test"}}' \
      | target/release/kb-mcp serve

### "KB directory not found"

    # Verify .kb exists
    ls -la /path/to/project/.kogral

    # Initialize if missing
    cd /path/to/project
    kb init

### "Permission denied"

    # Make binary executable
    chmod +x target/release/kb-mcp

    # Check environment variable
    echo $KOGRAL_DIR

### "Empty search results"

    # Add some test content
    kb add note "Test Note" --content "Test content"

    # Try search again in Claude Code

## Next Steps

- Read: MCP Tools API Reference for all available tools
- Explore: Use Cases for more examples
- Configure: Configuration Reference to customize behavior
- Integrate: Claude Code Integration for advanced setup

## Quick Reference Card

Task | Say to Claude
--- | ---
Search | "Find notes about X"
Add note | "Add a note about X with tags Y, Z"
Add decision | "Document decision to use X for Y"
Get guidelines | "What are our X guidelines?"
Link nodes | "Link A to B as implements"
Summarize | "Summarize project knowledge"
Export | "Export to Logseq format"

Remember: Claude Code with MCP turns KOGRAL into an active participant in your development workflow. Ask questions, capture decisions, and let AI help you maintain your project's knowledge graph.

# Claude Code Integration

# Logseq Integration

# Obsidian Compatibility

# Git Workflows

# MCP Protocol

# MCP Tools API Reference

Complete reference for the Model Context Protocol (MCP) server tools and resources.

## Overview

The kogral-mcp server implements the MCP protocol (JSON-RPC 2.0) for Claude Code integration. It provides:

- 10 Tools: Operations for querying and modifying knowledge base (7 core + 3 block tools)
- 6 Resources: Access to knowledge graph content via URIs
- 2 Prompts: Guided workflows for common tasks

## Server Configuration

### Start MCP Server

    # Stdio transport (local use)
    kb serve

    # Or run directly
    kb-mcp serve

### Claude Code Configuration

Add to `~/.config/claude/config.json`:

    {
      "mcpServers": {
        "kogral-mcp": {
          "command": "/path/to/kb-mcp",
          "args": ["serve"],
          "env": {
            "KOGRAL_DIR": "/path/to/project/.kogral"
          }
        }
      }
    }

## Tools

### kb/search

Search the knowledge base using text and/or semantic similarity.

Input Schema:

    {
      "type": "object",
      "properties": {
        "query": {
          "type": "string",
          "description": "Search query"
        },
        "type": {
          "type": "string",
          "enum": ["note", "decision", "guideline", "pattern", "journal", "execution", "all"],
          "description": "Filter by node type",
          "default": "all"
        },
        "project": {
          "type": "string",
          "description": "Limit search to specific project graph"
        },
        "semantic": {
          "type": "boolean",
          "description": "Enable semantic similarity search",
          "default": true
        },
        "threshold": {
          "type": "number",
          "description": "Minimum similarity threshold (0-1)",
          "default": 0.4
        },
        "limit": {
          "type": "integer",
          "description": "Maximum number of results",
          "default": 10
        }
      },
      "required": ["query"]
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 1,
      "method": "kogral/search",
      "params": {
        "query": "error handling patterns in Rust",
        "type": "pattern",
        "semantic": true,
        "threshold": 0.6,
        "limit": 5
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "type": "text",
        "text": "Found 3 result(s):\n\n1. Error Handling with thiserror (pattern, score: 0.85)\n   Tags: rust, error-handling\n   Created: 2026-01-15\n   \n2. Result Type Best Practices (guideline, score: 0.72)\n   Tags: rust, error-handling, best-practices\n   Created: 2026-01-10\n\n3. Custom Error Types (note, score: 0.65)\n   Tags: rust, error-handling\n   Created: 2026-01-08"
      }
    }

### kb/add_note

Add a new note to the knowledge base.

Input Schema:

    {
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "description": "Note title"
        },
        "content": {
          "type": "string",
          "description": "Note content (markdown)"
        },
        "tags": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Tags for categorization",
          "default": []
        },
        "relates_to": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Related node IDs",
          "default": []
        },
        "project": {
          "type": "string",
          "description": "Project graph name",
          "default": "default"
        }
      },
      "required": ["title", "content"]
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 2,
      "method": "kogral/add_note",
      "params": {
        "title": "Async Trait Patterns",
        "content": "Common patterns for using async traits in Rust:\n\n1. Use `async-trait` crate\n2. Box return types for flexibility\n3. Consider Send + Sync bounds",
        "tags": ["rust", "async", "patterns"],
        "relates_to": ["pattern-error-handling"]
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 2,
      "result": {
        "type": "text",
        "text": "Note added successfully: note-async-trait-patterns (ID: note-abc123)"
      }
    }

### kb/add_decision

Create an Architectural Decision Record (ADR).

Input Schema:

    {
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "description": "Decision title"
        },
        "context": {
          "type": "string",
          "description": "Decision context and background"
        },
        "decision": {
          "type": "string",
          "description": "The decision made"
        },
        "consequences": {
          "type": "array",
          "items": { "type": "string" },
          "description": "List of consequences",
          "default": []
        },
        "status": {
          "type": "string",
          "enum": ["proposed", "accepted", "rejected", "deprecated", "superseded"],
          "default": "proposed"
        },
        "tags": {
          "type": "array",
          "items": { "type": "string" },
          "default": []
        }
      },
      "required": ["title", "context", "decision"]
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 3,
      "method": "kogral/add_decision",
      "params": {
        "title": "Use SurrealDB for Knowledge Graph Storage",
        "context": "Need a scalable storage solution that supports graph queries and can handle growing knowledge base",
        "decision": "Adopt SurrealDB as the primary storage backend for production deployments",
        "consequences": [
          "Better query performance for graph traversal",
          "Native support for relationships",
          "Additional infrastructure dependency",
          "Team needs to learn SurrealDB query language"
        ],
        "status": "accepted",
        "tags": ["architecture", "storage", "surrealdb"]
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 3,
      "result": {
        "type": "text",
        "text": "Decision added: decision-use-surrealdb (ID: decision-xyz789)\nStatus: accepted"
      }
    }

### kb/link

Create a relationship between two nodes.

Input Schema:

    {
      "type": "object",
      "properties": {
        "from": {
          "type": "string",
          "description": "Source node ID"
        },
        "to": {
          "type": "string",
          "description": "Target node ID"
        },
        "relation": {
          "type": "string",
          "enum": ["relates_to", "depends_on", "implements", "extends", "supersedes", "explains"],
          "description": "Relationship type"
        },
        "strength": {
          "type": "number",
          "description": "Relationship strength (0-1)",
          "minimum": 0,
          "maximum": 1,
          "default": 1.0
        }
      },
      "required": ["from", "to", "relation"]
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 4,
      "method": "kogral/link",
      "params": {
        "from": "note-async-trait-patterns",
        "to": "pattern-error-handling",
        "relation": "relates_to",
        "strength": 0.8
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 4,
      "result": {
        "type": "text",
        "text": "Link created: note-async-trait-patterns --[relates_to]--> pattern-error-handling (strength: 0.8)"
      }
    }

Relationship Types:

- `relates_to`: General conceptual relationship
- `depends_on`: Dependency (from depends on to)
- `implements`: Implementation of concept/pattern
- `extends`: Inherits or extends another node
- `supersedes`: Replaces an older version
- `explains`: Provides documentation/clarification

### kb/get_guidelines

Retrieve guidelines for current project with inheritance resolution.

Input Schema:

    {
      "type": "object",
      "properties": {
        "language": {
          "type": "string",
          "description": "Programming language (e.g., rust, nushell)"
        },
        "category": {
          "type": "string",
          "description": "Guideline category (e.g., error-handling, testing)"
        },
        "include_base": {
          "type": "boolean",
          "description": "Include shared/base guidelines",
          "default": true
        }
      }
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 5,
      "method": "kogral/get_guidelines",
      "params": {
        "language": "rust",
        "category": "error-handling",
        "include_base": true
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 5,
      "result": {
        "type": "text",
        "text": "Guidelines for rust/error-handling:\n\n## Project Guidelines (priority: 150)\n\n1. Custom Error Types (guideline-custom-errors)\n   - Use thiserror for error definitions\n   - Implement From traits for conversions\n   - Source: .kogral/guidelines/rust-errors.md\n\n## Shared Guidelines (priority: 50)\n\n1. Result Type Best Practices (guideline-result-best-practices)\n   - Always use Result<T> for fallible operations\n   - Never use unwrap() in production\n   - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md\n\n2. Error Propagation (guideline-error-propagation)\n   - Use ? operator for error propagation\n   - Add context with .context()\n   - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md"
      }
    }

### kb/list_graphs

List available knowledge graphs.

Input Schema:

    {
      "type": "object",
      "properties": {}
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 6,
      "method": "kb/list_graphs"
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 6,
      "result": {
        "type": "text",
        "text": "Available graphs:\n\n- default (Current project)\n  Path: /path/to/project/.kogral\n  Nodes: 42\n  Last modified: 2026-01-17T10:30:00Z\n\n- shared (Shared guidelines)\n  Path: ~/Tools/.kogral-shared\n  Nodes: 156\n  Last modified: 2026-01-15T14:20:00Z"
      }
    }

### kb/export

Export knowledge base to various formats.

Input Schema:

    {
      "type": "object",
      "properties": {
        "format": {
          "type": "string",
          "enum": ["logseq", "json", "markdown"],
          "description": "Export format"
        },
        "output_path": {
          "type": "string",
          "description": "Output file or directory path"
        },
        "include_types": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": ["note", "decision", "guideline", "pattern", "journal", "execution"]
          },
          "description": "Node types to include",
          "default": ["note", "decision", "guideline", "pattern"]
        },
        "skip_journals": {
          "type": "boolean",
          "default": true
        }
      },
      "required": ["format", "output_path"]
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 7,
      "method": "kogral/export",
      "params": {
        "format": "logseq",
        "output_path": "/Users/akasha/logseq-graph",
        "include_types": ["note", "decision", "guideline"],
        "skip_journals": true
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 7,
      "result": {
        "type": "text",
        "text": "Export completed:\n\nFormat: Logseq\nOutput: /Users/akasha/logseq-graph\nExported: 42 nodes\n  - 23 notes\n  - 12 decisions\n  - 7 guidelines\n\nLogseq graph ready to open."
      }
    }

## Block Tools

Tools for querying Logseq content blocks. Requires `blocks.enable_mcp_tools = true` in configuration.

### kb/find_blocks

Find blocks by tag, task status, or custom property across the knowledge base.

Input Schema:

    {
      "type": "object",
      "properties": {
        "tag": {
          "type": "string",
          "description": "Find blocks with this tag (e.g., 'card', 'important')"
        },
        "status": {
          "type": "string",
          "enum": ["TODO", "DOING", "DONE", "LATER", "NOW", "WAITING", "CANCELLED"],
          "description": "Find blocks with this task status"
        },
        "property_key": {
          "type": "string",
          "description": "Custom property key to search for"
        },
        "property_value": {
          "type": "string",
          "description": "Custom property value to match"
        },
        "limit": {
          "type": "integer",
          "description": "Maximum number of results",
          "default": 20
        }
      }
    }

Note: Provide one of: `tag`, `status`, or both `property_key` and `property_value`.

Example Request (find blocks by tag):

    {
      "jsonrpc": "2.0",
      "id": 8,
      "method": "kogral/find_blocks",
      "params": {
        "tag": "high-priority",
        "limit": 10
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 8,
      "result": {
        "type": "text",
        "text": "Found 3 blocks with tag '#high-priority':\n\n**Project Tasks** (project-tasks-123)\n  - TODO Implement authentication #high-priority\n  - TODO Fix security vulnerability #high-priority\n\n**Sprint Planning** (sprint-planning-456)\n  - DOING Refactor database layer #high-priority"
      }
    }

Example Request (find blocks by property):

    {
      "jsonrpc": "2.0",
      "id": 9,
      "method": "kogral/find_blocks",
      "params": {
        "property_key": "priority",
        "property_value": "high",
        "limit": 15
      }
    }

### kb/find_todos

Find all TODO blocks across the knowledge base.

Input Schema:

    {
      "type": "object",
      "properties": {
        "limit": {
          "type": "integer",
          "description": "Maximum number of results",
          "default": 20
        }
      }
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 10,
      "method": "kogral/find_todos",
      "params": {
        "limit": 25
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 10,
      "result": {
        "type": "text",
        "text": "Found 8 TODO blocks:\n\n**Project Tasks** (project-tasks-123)\n  - TODO Implement authentication\n  - TODO Write integration tests\n  - TODO Update documentation\n\n**Bug Fixes** (bug-fixes-456)\n  - TODO Fix race condition in cache\n  - TODO Address memory leak\n\n**Research** (research-789)\n  - TODO Evaluate GraphQL alternatives\n  - TODO Benchmark new approach\n  - TODO Document findings"
      }
    }

### kb/find_cards

Find all flashcard blocks (blocks tagged with #card) for spaced repetition learning.

Input Schema:

    {
      "type": "object",
      "properties": {
        "limit": {
          "type": "integer",
          "description": "Maximum number of flashcards",
          "default": 10
        }
      }
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 11,
      "method": "kogral/find_cards",
      "params": {
        "limit": 5
      }
    }

Example Response:

    {
      "jsonrpc": "2.0",
      "id": 11,
      "result": {
        "type": "text",
        "text": "Found 3 flashcards:\n\n**Rust Learning** (rust-learning-123)\n  - What is Rust's ownership model? #card #rust\n    - Ownership prevents data races at compile time\n    - Each value has a single owner\n\n**System Design** (system-design-456)\n  - What is the CAP theorem? #card #distributed-systems\n    - Consistency, Availability, Partition tolerance\n    - Can only guarantee 2 of 3\n\n**Algorithms** (algorithms-789)\n  - What is the time complexity of quicksort? #card #algorithms\n    - Average: O(n log n)\n    - Worst case: O(n²)"
      }
    }

Use Cases:

- kb/find_blocks: General block search by metadata
- kb/find_todos: Task management and tracking
- kb/find_cards: Spaced repetition learning system

See Also:

- Logseq Blocks Support
- ADR-004: Logseq Blocks Support

## Resources

Resources provide read-only access to knowledge graph content via URIs.

### kogral://project/notes

Access project notes.

URI: `kogral://project/notes` or `kogral://project/notes/{note-id}`

Example: Read all project notes

    kogral://project/notes

Example: Read specific note

    kogral://project/notes/async-trait-patterns

### kogral://project/decisions

Access project decisions (ADRs).

URI: `kogral://project/decisions` or `kogral://project/decisions/{decision-id}`

### kogral://project/guidelines

Access project-specific guidelines.

URI: `kogral://project/guidelines` or `kogral://project/guidelines/{guideline-id}`

### kogral://project/patterns

Access project patterns.

URI: `kogral://project/patterns` or `kogral://project/patterns/{pattern-id}`

### kogral://shared/guidelines

Access shared guidelines (inherited).

URI: `kogral://shared/guidelines` or `kogral://shared/guidelines/{guideline-id}`

### kogral://shared/patterns

Access shared patterns (inherited).

URI: `kogral://shared/patterns` or `kogral://shared/patterns/{pattern-id}`

## Prompts

Prompts provide guided workflows for common tasks.

### kb/summarize_project

Generate a comprehensive project knowledge summary.

Arguments:

    {
      "project": {
        "type": "string",
        "description": "Project graph name",
        "default": "default"
      }
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 8,
      "method": "kogral/summarize_project",
      "params": {
        "project": "default"
      }
    }

Returns: Prompt messages with project summary including:

- Total node counts by type
- Recent additions
- Top tags
- Key decisions
- Active patterns

### kb/find_related

Find nodes related to a specific topic or node.

Arguments:

    {
      "node_id": {
        "type": "string",
        "description": "Node ID to find relations for"
      },
      "depth": {
        "type": "integer",
        "description": "Maximum traversal depth",
        "default": 2
      }
    }

Example Request:

    {
      "jsonrpc": "2.0",
      "id": 9,
      "method": "kb/find_related",
      "params": {
        "node_id": "pattern-error-handling",
        "depth": 2
      }
    }

Returns: Prompt messages with:

- Direct relationships
- Indirect relationships (depth 2+)
- Common tags
- Related guidelines

## Error Handling

### Error Codes

Standard JSON-RPC 2.0 error codes:

Code | Meaning | Description
--- | --- | ---
-32700 | Parse error | Invalid JSON
-32600 | Invalid Request | Missing required fields
-32601 | Method not found | Unknown tool/resource
-32602 | Invalid params | Parameter validation failed
-32603 | Internal error | Server-side error

### Example Error Response

    {
      "jsonrpc": "2.0",
      "id": 1,
      "error": {
        "code": -32602,
        "message": "Invalid params",
        "data": {
          "details": "Field 'query' is required but missing",
          "field": "query"
        }
      }
    }

## Best Practices

### 1. Use Semantic Search for Discovery

    {
      "method": "kogral/search",
      "params": {
        "query": "how to handle database connection errors",
        "semantic": true,
        "threshold": 0.5
      }
    }

### 2. Link Related Concepts

    {
      "method": "kogral/link",
      "params": {
        "from": "note-new-discovery",
        "to": "pattern-related-pattern",
        "relation": "implements"
      }
    }

### 3. Query Guidelines Before Implementation

    {
      "method": "kogral/get_guidelines",
      "params": {
        "language": "rust",
        "category": "testing"
      }
    }

### 4. Document Decisions with ADRs

    {
      "method": "kogral/add_decision",
      "params": {
        "title": "Use X for Y",
        "context": "Background...",
        "decision": "We will...",
        "consequences": ["Pro 1", "Con 1"]
      }
    }

## See Also

- MCP Specification
- Quick Start Guide
- Configuration Reference
- kb-mcp Source Code

# Resources

# Rust API

# Development Setup

# Code Standards

# Testing