Documentation
¶
Overview ¶
Package obsidian implements the Engram → Obsidian vault export engine. It reads observations from the store and writes structured markdown files with YAML frontmatter, wikilinks, and hub notes.
Index ¶
- func ObservationToMarkdown(obs store.Observation) string
- func SessionHubMarkdown(sessionID string, obs []ObsRef) string
- func ShouldCreateTopicHub(count int) bool
- func Slugify(title string, id int64) string
- func TopicHubMarkdown(prefix string, obs []ObsRef) string
- func WriteGraphConfig(vaultPath string, mode GraphConfigMode) error
- func WriteState(path string, s SyncState) error
- type ExportConfig
- type ExportResult
- type Exportable
- type Exporter
- type GraphConfigMode
- type ObsRef
- type StoreReader
- type SyncState
- type Watcher
- type WatcherConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ObservationToMarkdown ¶
func ObservationToMarkdown(obs store.Observation) string
ObservationToMarkdown converts a store.Observation into an Obsidian-compatible markdown string with YAML frontmatter, an H1 title, the content body, and a wikilinks footer section.
func SessionHubMarkdown ¶
SessionHubMarkdown generates the markdown content for a session hub note. It lists all observations in the session as wikilinks.
Output path: {vault}/engram/_sessions/{sessionID}.md
func ShouldCreateTopicHub ¶
ShouldCreateTopicHub reports whether a topic prefix has enough observations to warrant creating a hub note. The threshold is ≥2 (REQ-EXPORT-05).
func Slugify ¶
Slugify converts an observation title and ID into a filesystem-safe slug. The algorithm:
- Lowercase the title
- Replace non-alphanumeric characters with hyphens
- Trim leading/trailing hyphens
- Truncate to 60 chars (trimming trailing hyphens after truncation)
- Append the ID for collision safety
If the title is empty, the slug is "observation-{id}".
func TopicHubMarkdown ¶
TopicHubMarkdown generates the markdown content for a topic cluster hub note. It lists all observations sharing the same topic prefix as wikilinks with type annotations.
Output path: {vault}/engram/_topics/{prefix}.md where prefix uses "--" instead of "/" for filesystem safety.
func WriteGraphConfig ¶
func WriteGraphConfig(vaultPath string, mode GraphConfigMode) error
WriteGraphConfig writes the embedded graph.json default into {vaultPath}/.obsidian/graph.json according to the given mode.
- preserve: creates the file only when it does not already exist.
- force: always creates or overwrites the file with the embedded default.
- skip: no-op; returns nil immediately.
The .obsidian/ directory is created with 0755 permissions if it does not exist (except in skip mode where nothing is written).
func WriteState ¶
WriteState persists the sync state as JSON to the given file path. The file is written atomically (overwrite) with 0644 permissions.
Types ¶
type ExportConfig ¶
type ExportConfig struct {
VaultPath string // --vault (required): path to the Obsidian vault root
Project string // --project (optional): filter export to a single project
Limit int // --limit (0 = no limit)
Since time.Time // --since (zero = use state file)
Force bool // --force: ignore state, full re-export
GraphConfig GraphConfigMode // --graph-config: preserve|force|skip (empty string = skip for backward compat)
}
ExportConfig holds all CLI flags for the obsidian-export command.
type ExportResult ¶
type ExportResult struct {
Created int
Updated int
Deleted int
Skipped int
HubsCreated int
Errors []error
}
ExportResult summarizes what happened during an export run.
type Exportable ¶
type Exportable interface {
// Export performs one export cycle and returns its result.
Export() (*ExportResult, error)
// SetGraphConfig overrides the GraphConfig mode for subsequent calls.
SetGraphConfig(mode GraphConfigMode)
// GraphConfig returns the current GraphConfig mode.
GraphConfig() GraphConfigMode
}
Exportable is the interface the Watcher uses to run export cycles. *Exporter satisfies this interface. Fake implementations are used in tests.
type Exporter ¶
type Exporter struct {
// contains filtered or unexported fields
}
Exporter reads from the store and writes markdown files to a vault.
func NewExporter ¶
func NewExporter(s StoreReader, cfg ExportConfig) *Exporter
NewExporter constructs an Exporter. Validation happens in Export().
func (*Exporter) Export ¶
func (e *Exporter) Export() (*ExportResult, error)
Export performs a full or incremental export from the store to the vault. It returns an ExportResult summarizing what happened.
func (*Exporter) GraphConfig ¶
func (e *Exporter) GraphConfig() GraphConfigMode
GraphConfig returns the current GraphConfig mode.
func (*Exporter) SetGraphConfig ¶
func (e *Exporter) SetGraphConfig(mode GraphConfigMode)
SetGraphConfig sets the GraphConfig mode on this exporter's config. This is used by Watcher to force GraphConfigSkip on subsequent cycles (REQ-WATCH-06).
type GraphConfigMode ¶
type GraphConfigMode string
GraphConfigMode controls how WriteGraphConfig handles an existing graph.json.
const ( // GraphConfigPreserve writes the default template only if graph.json is absent (default). GraphConfigPreserve GraphConfigMode = "preserve" // GraphConfigForce always overwrites graph.json with the embedded default. GraphConfigForce GraphConfigMode = "force" // GraphConfigSkip never reads, writes, or creates graph.json. GraphConfigSkip GraphConfigMode = "skip" )
func ParseGraphConfigMode ¶
func ParseGraphConfigMode(s string) (GraphConfigMode, error)
ParseGraphConfigMode parses s into a GraphConfigMode. Returns an error for any value not in the accepted set {preserve, force, skip}. Parsing is case-sensitive.
type ObsRef ¶
type ObsRef struct {
Slug string // filename slug (without .md extension), e.g. "fixed-auth-bug-1"
Title string // human-readable title
TopicKey string // observation's topic_key (may be empty)
Type string // observation type (e.g. "bugfix", "architecture")
}
ObsRef is a lightweight reference to an observation for use in hub notes. It carries only the fields needed to build wikilinks and type annotations.
type StoreReader ¶
type StoreReader interface {
Export() (*store.ExportData, error)
Stats() *store.Stats
}
StoreReader is the read-only interface the exporter needs. Keeps the dependency narrow — easy to mock in tests.
type SyncState ¶
type SyncState struct {
LastExportAt string `json:"last_export_at"`
Files map[int64]string `json:"files"` // obs ID → relative vault path
SessionHubs map[string]string `json:"session_hubs"` // session ID → relative path
TopicHubs map[string]string `json:"topic_hubs"` // topic prefix → relative path
Version int `json:"version"` // schema version (1)
}
SyncState tracks the state of a previous export run. It is persisted as JSON in {vault}/engram/.engram-sync-state.json.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher wraps an Exportable and runs it on a fixed interval in a loop.
func NewWatcher ¶
func NewWatcher(cfg WatcherConfig) *Watcher
NewWatcher constructs a Watcher from the given config.
func (*Watcher) Run ¶
Run executes the watch loop.
Behavior:
- The first cycle runs immediately (REQ-WATCH-03).
- Subsequent cycles run after each interval tick.
- On cycle error: the error is logged and the loop continues (REQ-WATCH-04).
- The first cycle uses the exporter's current GraphConfig; subsequent cycles force GraphConfigSkip (REQ-WATCH-06).
- Returns ctx.Err() when the context is canceled or times out (REQ-WATCH-05).
type WatcherConfig ¶
type WatcherConfig struct {
// Exporter is the export driver. Required.
Exporter Exportable
// Interval between cycles. Required.
Interval time.Duration
// Logf is the log function. Defaults to log.Printf if nil.
Logf func(format string, args ...any)
}
WatcherConfig holds constructor parameters for Watcher.