feat: Implement foundation layer with domain entities and repository interfaces

- Add complete domain layer: Note, Vault, WikiLink, Tag, Frontmatter, Graph entities
- Implement repository interfaces for data access abstraction
- Create comprehensive configuration system with YAML and env support
- Add CLI entry point with signal handling and graceful shutdown
- Fix mermaid diagram syntax in design.md (array notation)
- Add CLAUDE.md for development guidance

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev
2025-10-08 10:22:28 +04:00
parent b655c58ba1
commit da289d4a7e
24 changed files with 1840 additions and 7 deletions

View File

@@ -0,0 +1,33 @@
package repository
import (
"context"
"time"
)
type GitStatus struct {
Branch string
Modified []string
Untracked []string
Staged []string
Ahead int
Behind int
}
type Commit struct {
Hash string
Author string
Date time.Time
Message string
Files []string
}
type GitRepository interface {
Status(ctx context.Context) (*GitStatus, error)
Pull(ctx context.Context) error
Push(ctx context.Context) error
Commit(ctx context.Context, message string, files []string) error
Log(ctx context.Context, path string, limit int) ([]*Commit, error)
IsEnabled() bool
Clone(ctx context.Context, url string, path string) error
}

View File

@@ -0,0 +1,40 @@
package repository
import (
"context"
"github.com/user/obsidian-mcp-server/internal/domain"
)
type GraphNode struct {
Path string `json:"path"`
Title string `json:"title"`
}
type GraphEdge struct {
From string `json:"from"`
To string `json:"to"`
}
type GraphData struct {
Nodes []*GraphNode `json:"nodes"`
Edges []*GraphEdge `json:"edges"`
}
type BacklinkInfo struct {
From string `json:"from"`
Context string `json:"context"`
Line int `json:"line"`
}
type GraphIndex interface {
AddNote(ctx context.Context, note *domain.Note) error
RemoveNote(ctx context.Context, path string) error
GetBacklinks(ctx context.Context, path string) ([]*BacklinkInfo, error)
GetOutlinks(ctx context.Context, path string) ([]string, error)
GetConnected(ctx context.Context, path string, depth int) (*GraphData, error)
FindBrokenLinks(ctx context.Context, notePath string) ([]string, error)
UpdateLinks(ctx context.Context, oldPath, newPath string) error
Rebuild(ctx context.Context, notes []*domain.Note) error
Clear() error
}

View File

@@ -0,0 +1,18 @@
package repository
import (
"context"
"github.com/user/obsidian-mcp-server/internal/domain"
)
type NoteRepository interface {
Get(ctx context.Context, path string) (*domain.Note, error)
Create(ctx context.Context, note *domain.Note) error
Update(ctx context.Context, note *domain.Note) error
Delete(ctx context.Context, path string) error
List(ctx context.Context, pattern string, recursive bool) ([]*domain.Note, error)
Exists(ctx context.Context, path string) bool
FindByTag(ctx context.Context, tag string) ([]*domain.Note, error)
FindByContent(ctx context.Context, query string) ([]*domain.Note, error)
}

View File

@@ -0,0 +1,36 @@
package repository
import (
"context"
"github.com/user/obsidian-mcp-server/internal/domain"
)
type SearchResult struct {
Path string `json:"path"`
Score float64 `json:"score"`
Matches []*SearchMatch `json:"matches"`
}
type SearchMatch struct {
Line int `json:"line"`
Content string `json:"content"`
ContextBefore string `json:"context_before"`
ContextAfter string `json:"context_after"`
}
type SearchQuery struct {
Query string
MaxResults int
CaseSensitive bool
UseRegex bool
ContextLines int
}
type SearchIndex interface {
Index(ctx context.Context, note *domain.Note) error
Remove(ctx context.Context, path string) error
Search(ctx context.Context, query *SearchQuery) ([]*SearchResult, error)
Rebuild(ctx context.Context, notes []*domain.Note) error
Clear() error
}

View File

@@ -0,0 +1,30 @@
package repository
import (
"context"
"github.com/user/obsidian-mcp-server/internal/domain"
)
type TagInfo struct {
Name string `json:"name"`
Count int `json:"count"`
Nested bool `json:"nested"`
}
type TagFilter struct {
Tags []string
Mode string // "all" or "any"
IncludeNested bool
}
type TagIndex interface {
AddNote(ctx context.Context, note *domain.Note) error
RemoveNote(ctx context.Context, path string) error
GetAllTags(ctx context.Context) ([]*TagInfo, error)
GetNotesWithTag(ctx context.Context, tag string) ([]string, error)
GetNotesWithTags(ctx context.Context, filter *TagFilter) ([]string, error)
RenameTag(ctx context.Context, oldTag, newTag string) error
Rebuild(ctx context.Context, notes []*domain.Note) error
Clear() error
}

View File

@@ -0,0 +1,39 @@
package repository
import (
"context"
"github.com/user/obsidian-mcp-server/internal/domain"
)
type VaultStructure struct {
Name string `json:"name"`
Path string `json:"path"`
Type string `json:"type"` // "file" or "directory"
Children []*VaultStructure `json:"children,omitempty"`
}
type MostLinkedNote struct {
Path string `json:"path"`
Count int `json:"count"`
}
type VaultStats struct {
TotalNotes int `json:"total_notes"`
TotalWords int `json:"total_words"`
TotalLinks int `json:"total_links"`
TotalTags int `json:"total_tags"`
AvgNoteLength int `json:"avg_note_length"`
MostLinked []*MostLinkedNote `json:"most_linked"`
OrphanedNotes int `json:"orphaned_notes"`
LastIndexed string `json:"last_indexed"`
}
type VaultRepository interface {
GetStats(ctx context.Context) (*VaultStats, error)
GetStructure(ctx context.Context, maxDepth int) (*VaultStructure, error)
FindOrphaned(ctx context.Context, excludePaths []string) ([]string, error)
GetMostLinked(ctx context.Context, limit int) ([]*MostLinkedNote, error)
Initialize(ctx context.Context, vault *domain.Vault) error
Rebuild(ctx context.Context) error
}