mindnet_obsidian/docs/03_Entwicklerhandbuch.md
Lars a9b3e2f0e2
Some checks are pending
Node.js build / build (20.x) (push) Waiting to run
Node.js build / build (22.x) (push) Waiting to run
Implement Chain Workbench and Vault Triage features in Mindnet plugin
- Introduced new workflows for Chain Workbench and Vault Triage, enhancing user capabilities for managing template matches and identifying chain gaps.
- Added commands for opening the Chain Workbench and scanning the vault for chain gaps, improving the overall functionality of the plugin.
- Updated documentation to include detailed instructions for the new workflows, ensuring users can effectively utilize the features.
- Enhanced the UI for both the Chain Workbench and Vault Triage Scan, providing a more intuitive user experience.
- Implemented tests for the new functionalities to ensure reliability and accuracy in various scenarios.
2026-01-26 10:51:12 +01:00

22 KiB

Mindnet Causal Assistant - Entwicklerhandbuch

Version: 1.0.0
Stand: 2025-01-XX
Zielgruppe: Entwickler, die am Plugin arbeiten oder es erweitern


Inhaltsverzeichnis

  1. Überblick
  2. Projekt-Struktur
  3. Entwicklungsumgebung
  4. Code-Architektur
  5. Hauptmodule
  6. Erweiterungen entwickeln
  7. Testing
  8. Build & Deployment

Überblick

Das Mindnet Causal Assistant Plugin ist ein Obsidian Community Plugin geschrieben in TypeScript, gebündelt mit esbuild zu einer einzelnen main.js Datei.

Technologie-Stack

  • Sprache: TypeScript (strict mode)
  • Bundler: esbuild
  • Package Manager: npm
  • Testing: Vitest
  • Linting: ESLint mit obsidianmd Plugin

Projekt-Typ

  • Obsidian Community Plugin
  • Entry Point: src/main.tsmain.js
  • Release-Artefakte: main.js, manifest.json, optional styles.css

Projekt-Struktur

mindnet_obsidian/
├── src/                          # TypeScript Source Code
│   ├── main.ts                   # Plugin Entry Point
│   ├── settings.ts               # Settings Interface & Defaults
│   ├── analysis/                 # Chain Inspector & Analysis
│   │   ├── chainInspector.ts     # Haupt-Analyse-Engine
│   │   ├── templateMatching.ts   # Template Matching Algorithmus
│   │   ├── graphIndex.ts         # Graph-Indexierung
│   │   ├── sectionContext.ts     # Section-Context-Handling
│   │   └── severityPolicy.ts     # Severity-Policy-Logik
│   ├── commands/                 # Command Implementations
│   │   ├── inspectChainsCommand.ts
│   │   └── fixFindingsCommand.ts
│   ├── dictionary/               # Config Loader & Parser
│   │   ├── ChainRolesLoader.ts
│   │   ├── ChainTemplatesLoader.ts
│   │   ├── DictionaryLoader.ts
│   │   ├── parseChainRoles.ts
│   │   ├── parseChainTemplates.ts
│   │   └── types.ts
│   ├── entityPicker/             # Entity Selection UI
│   │   ├── noteIndex.ts
│   │   ├── folderTree.ts
│   │   ├── filters.ts
│   │   ├── wikilink.ts
│   │   └── types.ts
│   ├── export/                   # Graph Export
│   │   ├── exportGraph.ts
│   │   └── types.ts
│   ├── graph/                    # Graph Building & Traversal
│   │   ├── GraphBuilder.ts
│   │   ├── GraphIndex.ts
│   │   ├── renderChainReport.ts
│   │   ├── resolveTarget.ts
│   │   └── traverse.ts
│   ├── interview/                # Interview Wizard
│   │   ├── InterviewConfigLoader.ts
│   │   ├── parseInterviewConfig.ts
│   │   ├── wizardState.ts
│   │   ├── loopState.ts
│   │   └── renderer.ts
│   ├── lint/                     # Linting Engine
│   │   ├── LintEngine.ts
│   │   └── rules/
│   ├── mapping/                  # Semantic Mapping
│   │   ├── semanticMappingBuilder.ts
│   │   ├── mappingExtractor.ts
│   │   ├── mappingBuilder.ts
│   │   └── edgeTypeSelector.ts
│   ├── parser/                   # Markdown Parsing
│   │   ├── parseEdgesFromCallouts.ts
│   │   ├── parseFrontmatter.ts
│   │   └── parseRelLinks.ts
│   ├── schema/                   # Graph Schema
│   ├── ui/                       # UI Components
│   │   ├── InterviewWizardModal.ts
│   │   ├── ProfileSelectionModal.ts
│   │   ├── MindnetSettingTab.ts
│   │   └── ...
│   ├── unresolvedLink/           # Unresolved Link Handling
│   ├── vocab/                    # Edge Vocabulary
│   └── tests/                    # Test Files
├── docs/                         # Dokumentation
├── scripts/                      # Build Scripts
│   └── deploy-local.ps1
├── package.json                  # Dependencies & Scripts
├── tsconfig.json                 # TypeScript Config
├── esbuild.config.mjs            # Build Config
├── manifest.json                 # Plugin Manifest
└── main.js                       # Generated Bundle (nicht committen)

Entwicklungsumgebung

Voraussetzungen

  • Node.js: LTS Version (18+ empfohlen)
  • npm: Inkludiert mit Node.js
  • Git: Für Versionierung
  • Obsidian Desktop: Für Testing

Setup

# Repository klonen
git clone <repository-url> mindnet_obsidian
cd mindnet_obsidian

# Dependencies installieren
npm install

# Development Build (Watch Mode)
npm run dev

# Production Build
npm run build

# Tests ausführen
npm run test

# Linting
npm run lint

Development Workflow

  1. Code ändern in src/
  2. Watch Mode läuft (npm.cmd run dev)
  3. Automatisches Rebuild bei Dateiänderungen
  4. Deploy lokal (npm.cmd run deploy:local oder npm.cmd run build:deploy)
  5. Obsidian Plugin reload (disable/enable)
  6. Testen

Code-Architektur

Plugin Lifecycle

Entry Point: src/main.ts

export default class MindnetCausalAssistantPlugin extends Plugin {
  settings: MindnetSettings;
  
  async onload(): Promise<void> {
    await this.loadSettings();
    this.addSettingTab(new MindnetSettingTab(this.app, this));
    // Register commands, event handlers, etc.
  }
  
  onunload(): void {
    // Cleanup
  }
}

Settings Management

Datei: src/settings.ts

export interface MindnetSettings {
  edgeVocabularyPath: string;
  graphSchemaPath: string;
  // ... weitere Settings
}

export const DEFAULT_SETTINGS: MindnetSettings = {
  edgeVocabularyPath: "_system/dictionary/edge_vocabulary.md",
  // ... Defaults
};

Laden/Speichern:

async loadSettings(): Promise<void> {
  this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}

async saveSettings(): Promise<void> {
  await this.saveData(this.settings);
}

Command Registration

Pattern:

this.addCommand({
  id: "mindnet-command-id",
  name: "Mindnet: Command Name",
  callback: async () => {
    // Implementation
  },
});

Editor Callbacks:

this.addCommand({
  id: "mindnet-editor-command",
  name: "Mindnet: Editor Command",
  editorCallback: async (editor) => {
    // Editor-basierte Implementation
  },
});

Hauptmodule

1. Analysis (src/analysis/)

Zweck: Chain Inspector & Template Matching

Hauptdateien:

  • chainInspector.ts - Haupt-Analyse-Engine
  • templateMatching.ts - Template Matching Algorithmus
  • graphIndex.ts - Graph-Indexierung für Traversal
  • sectionContext.ts - Section-Context-Extraktion
  • severityPolicy.ts - Severity-Policy-Logik

Verwendung:

import { executeInspectChains } from "./commands/inspectChainsCommand";

await executeInspectChains(
  app,
  editor,
  activeFile.path,
  chainRoles,
  settings,
  {},
  chainTemplates,
  templatesLoadResult
);

2. Dictionary (src/dictionary/)

Zweck: Config Loading & Parsing

Hauptdateien:

  • ChainRolesLoader.ts - Lädt chain_roles.yaml
  • ChainTemplatesLoader.ts - Lädt chain_templates.yaml
  • DictionaryLoader.ts - Basis-Loader mit Error-Handling
  • parseChainRoles.ts - YAML → ChainRolesConfig Parser
  • parseChainTemplates.ts - YAML → ChainTemplatesConfig Parser

Pattern:

const result = await ChainRolesLoader.load(
  app,
  settings.chainRolesPath,
  lastKnownGood
);

if (result.data !== null) {
  // Use result.data
}

Last-Known-Good:

  • Bei Fehlern wird letzte gültige Config verwendet
  • lastKnownGood Parameter für Fallback

3. Graph (src/graph/)

Zweck: Graph Building & Traversal

Hauptdateien:

  • GraphBuilder.ts - Baut Graph aus Vault
  • GraphIndex.ts - Indexiert Edges für Traversal
  • traverse.ts - BFS/DFS Traversal
  • resolveTarget.ts - Link-Target-Auflösung
  • renderChainReport.ts - Report-Generierung

Verwendung:

const graph = await buildGraph(app, vocabulary);
const index = buildIndex(graph.edges);
const paths = traverseForward(index, startId, maxHops, maxPaths);

4. Interview (src/interview/)

Zweck: Interview Wizard für Note-Erstellung

Hauptdateien:

  • InterviewConfigLoader.ts - Lädt interview_config.yaml
  • parseInterviewConfig.ts - YAML → InterviewConfig Parser
  • wizardState.ts - Wizard State Management (Steps, Loops, Nested Loops)
  • loopState.ts - Loop State Management (nested loops)
  • renderer.ts - Output-Rendering (Section-basiert)
  • writeFrontmatter.ts - Frontmatter-Generierung
  • sectionKeyResolver.ts - Section-Key-Auflösung
  • extractTargetFromAnchor.ts - Target-Extraktion aus Anchors
  • slugify.ts - Slug-Generierung für Dateinamen
  • types.ts - Interview-Types

Verwendung:

import { InterviewConfigLoader } from "./interview/InterviewConfigLoader";
import { writeFrontmatter } from "./interview/writeFrontmatter";
import { InterviewWizardModal } from "./ui/InterviewWizardModal";

// Config laden
const result = await InterviewConfigLoader.loadConfig(app, configPath);
const config = result.config;

// Frontmatter schreiben
const frontmatter = writeFrontmatter({
  id: "note_123",
  title: "Meine Note",
  noteType: "experience",
  interviewProfile: "experience_basic",
  defaults: profile.defaults,
  frontmatterWhitelist: config.frontmatterWhitelist,
});

// Wizard starten
const modal = new InterviewWizardModal(app, profile, onFinish);
modal.open();

Features:

  • Nested Loops: Unterstützung für verschachtelte Loops
  • Section-basierte Ausgabe: Output wird in Sections geschrieben
  • Frontmatter-Whitelist: Erlaubte Frontmatter-Keys
  • Post-Run Actions: Automatische Actions nach Wizard (z.B. Edger)

5. Mapping (src/mapping/)

Zweck: Semantic Mapping Builder

Hauptdateien:

  • semanticMappingBuilder.ts - Haupt-Mapping-Builder
  • mappingExtractor.ts - Extrahiert existierende Mappings
  • mappingBuilder.ts - Baut neue Mappings
  • edgeTypeSelector.ts - Edge-Type-Selection UI

Verwendung:

import { buildSemanticMappings } from "./mapping/semanticMappingBuilder";

const result = await buildSemanticMappings(
  app,
  activeFile,
  settings,
  allowOverwrite,
  plugin
);

6. Parser (src/parser/)

Zweck: Markdown Parsing

Hauptdateien:

  • parseEdgesFromCallouts.ts - Extrahiert Edges aus Callouts
  • parseFrontmatter.ts - Frontmatter-Parsing
  • parseRelLinks.ts - Relative Link-Parsing

Verwendung:

import { parseEdgesFromCallouts } from "./parser/parseEdgesFromCallouts";

const edges = parseEdgesFromCallouts(content, file, vocabulary);

7. UI (src/ui/)

Zweck: UI Components (Modals, Views)

Hauptdateien:

  • InterviewWizardModal.ts - Interview Wizard UI
  • ProfileSelectionModal.ts - Profil-Auswahl UI
  • MindnetSettingTab.ts - Settings Tab
  • EdgeTypeChooserModal.ts - Edge-Type-Auswahl UI
  • EntityPickerModal.ts - Entity-Auswahl UI
  • AdoptNoteModal.ts - Note-Adoption-Confirmation
  • FolderTreeModal.ts - Folder-Auswahl UI
  • LinkPromptModal.ts - Link-Eingabe UI
  • InlineEdgeTypeModal.ts - Inline Edge-Type-Selection
  • ConfirmOverwriteModal.ts - Overwrite-Confirmation

Pattern:

export class MyModal extends Modal {
  constructor(app: App, onResult: (result: MyResult) => void) {
    super(app);
    this.onResult = onResult;
  }
  
  onOpen() {
    // Build UI
  }
  
  onClose() {
    // Cleanup
  }
}

8. Vocabulary (src/vocab/)

Zweck: Edge Vocabulary Management

Hauptdateien:

  • Vocabulary.ts - Wrapper-Klasse für Lookup-Methoden
  • VocabularyLoader.ts - Lädt Vocabulary-Datei
  • parseEdgeVocabulary.ts - Parst Markdown zu EdgeVocabulary
  • types.ts - Vocabulary-Types

Verwendung:

import { VocabularyLoader } from "./vocab/VocabularyLoader";
import { parseEdgeVocabulary } from "./vocab/parseEdgeVocabulary";
import { Vocabulary } from "./vocab/Vocabulary";

const text = await VocabularyLoader.loadText(app, path);
const parsed = parseEdgeVocabulary(text);
const vocabulary = new Vocabulary(parsed);

const canonical = vocabulary.getCanonical("ausgelöst_durch");
const normalized = vocabulary.normalize("causes");

9. Lint (src/lint/)

Zweck: Linting Engine für Note-Validierung

Hauptdateien:

  • LintEngine.ts - Haupt-Linting-Engine
  • rules/index.ts - Regel-Registry
  • rules/rule_hub_has_causality.ts - Kausalitäts-Prüfung
  • rules/rule_missing_target.ts - Fehlende Target-Prüfung
  • rules/rule_unkown_edge.ts - Unbekannte Edge-Type-Prüfung
  • types.ts - Lint-Types

Verwendung:

import { LintEngine } from "./lint/LintEngine";

const findings = await LintEngine.lintCurrentNote(
  app,
  vocabulary,
  { showCanonicalHints: true }
);

10. Export (src/export/)

Zweck: Graph Export zu JSON

Hauptdateien:

  • exportGraph.ts - Haupt-Export-Funktion
  • types.ts - Export-Types

Verwendung:

import { exportGraph } from "./export/exportGraph";

await exportGraph(app, vocabulary, "_system/exports/graph.json");

11. Schema (src/schema/)

Zweck: Graph Schema Loading

Hauptdateien:

  • GraphSchemaLoader.ts - Lädt Graph-Schema-Datei

Verwendung:

import { GraphSchemaLoader } from "./schema/GraphSchemaLoader";

const schema = await GraphSchemaLoader.load(app, schemaPath);
const typical = schema.getTypicalEdgeTypes("experience");

Zweck: Unresolved Link Handling

Hauptdateien:

  • unresolvedLinkHandler.ts - Haupt-Handler für Link-Clicks
  • linkHelpers.ts - Link-Parsing und -Normalisierung
  • adoptHelpers.ts - Note-Adoption-Logik

Verwendung:

import { isUnresolvedLink, normalizeLinkTarget } from "./unresolvedLink/linkHelpers";
import { isAdoptCandidate, evaluateAdoptionConfidence } from "./unresolvedLink/adoptHelpers";

const unresolved = isUnresolvedLink(app, linkTarget, sourcePath);
const confidence = evaluateAdoptionConfidence(file, content, maxChars, hint, windowMs);

13. Entity Picker (src/entityPicker/)

Zweck: Entity Selection (Notes, Folders)

Hauptdateien:

  • noteIndex.ts - Baut Index aller Notes
  • folderTree.ts - Folder-Tree-Struktur
  • filters.ts - Filter-Logik
  • wikilink.ts - Wikilink-Parsing
  • types.ts - Entity-Picker-Types

Verwendung:

import { buildNoteIndex } from "./entityPicker/noteIndex";
import { buildFolderTree } from "./entityPicker/folderTree";

const index = await buildNoteIndex(app);
const tree = buildFolderTree(app);

14. Commands (src/commands/)

Zweck: Command Implementations

Hauptdateien:

  • inspectChainsCommand.ts - Chain Inspector Command
  • fixFindingsCommand.ts - Fix Findings Command

Verwendung:

import { executeInspectChains } from "./commands/inspectChainsCommand";
import { executeFixFindings } from "./commands/fixFindingsCommand";

await executeInspectChains(app, editor, filePath, chainRoles, settings, {}, chainTemplates, result);
await executeFixFindings(app, editor, filePath, chainRoles, interviewConfig, settings, plugin);

Erweiterungen entwickeln

1. Neuen Command hinzufügen

Schritt 1: Command in main.ts registrieren

this.addCommand({
  id: "mindnet-my-command",
  name: "Mindnet: My Command",
  callback: async () => {
    // Implementation
  },
});

Schritt 2: Implementation in separater Datei (optional)

// src/commands/myCommand.ts
export async function executeMyCommand(
  app: App,
  settings: MindnetSettings
): Promise<void> {
  // Implementation
}

Schritt 3: In main.ts importieren und verwenden

import { executeMyCommand } from "./commands/myCommand";

this.addCommand({
  id: "mindnet-my-command",
  name: "Mindnet: My Command",
  callback: async () => {
    await executeMyCommand(this.app, this.settings);
  },
});

2. Neue Lint-Regel hinzufügen

Schritt 1: Regel-Datei erstellen

// src/lint/rules/rule_my_rule.ts
import type { LintRule, LintFinding } from "../types";

export const ruleMyRule: LintRule = {
  id: "my_rule",
  name: "My Rule",
  severity: "WARN",
  check: async (app, file, vocabulary): Promise<LintFinding[]> => {
    const findings: LintFinding[] = [];
    // Check logic
    return findings;
  },
};

Schritt 2: Regel registrieren

// src/lint/rules/index.ts
import { ruleMyRule } from "./rule_my_rule";

export const RULES = [
  ruleMyRule,
  // ... weitere Regeln
];

3. Neues Finding hinzufügen

Schritt 1: Finding-Code definieren

// In chainInspector.ts oder templateMatching.ts
const finding: Finding = {
  code: "my_finding",
  severity: "WARN",
  message: "My finding message",
  evidence: { /* ... */ },
};

Schritt 2: Finding generieren

if (condition) {
  findings.push({
    code: "my_finding",
    severity: "WARN",
    message: "My finding message",
    evidence: { /* ... */ },
  });
}

4. Neues UI-Element hinzufügen

Schritt 1: Modal/Component erstellen

// src/ui/MyModal.ts
import { Modal } from "obsidian";

export class MyModal extends Modal {
  constructor(app: App, onResult: (result: MyResult) => void) {
    super(app);
    this.onResult = onResult;
  }
  
  onOpen() {
    const { contentEl } = this;
    // Build UI
  }
  
  onClose() {
    const { contentEl } = this;
    contentEl.empty();
  }
}

Schritt 2: Modal verwenden

import { MyModal } from "./ui/MyModal";

new MyModal(this.app, (result) => {
  // Handle result
}).open();

Testing

Test-Setup

Framework: Vitest

Konfiguration: vitest.config.ts

Test-Dateien: src/tests/**/*.test.ts

Tests ausführen

# Alle Tests
npm run test

# Watch Mode
npm run test -- --watch

# Spezifische Datei
npm run test -- src/tests/analysis/chainInspector.test.ts

Test-Pattern

import { describe, it, expect } from "vitest";

describe("MyModule", () => {
  it("should do something", () => {
    const result = myFunction(input);
    expect(result).toBe(expected);
  });
});

Mocking

Obsidian API Mocking:

// src/__mocks__/obsidian.ts
export const mockApp = {
  vault: { /* ... */ },
  metadataCache: { /* ... */ },
  // ...
};

Build & Deployment

Development Build

npm run dev

Ergebnis:

  • main.js wird erstellt (mit Source Maps)
  • Watch Mode aktiviert
  • Automatisches Rebuild bei Änderungen

Production Build

npm run build

Ergebnis:

  • main.js wird erstellt (minified, ohne Source Maps)
  • TypeScript-Check wird ausgeführt
  • Tree-Shaking aktiviert

Lokales Deployment

Windows (PowerShell):

npm run deploy:local
# oder
powershell -ExecutionPolicy Bypass -File scripts/deploy-local.ps1

Script: scripts/deploy-local.ps1

  • Kopiert main.js, manifest.json nach Vault Plugin-Ordner
  • Optional: styles.css falls vorhanden

Zielpfad:

<vault>/.obsidian/plugins/mindnet-causal-assistant/

Release-Prozess

  1. Version bumpen:

    npm version patch|minor|major
    
    • Aktualisiert manifest.json und package.json
    • Fügt Eintrag zu versions.json hinzu
  2. Build:

    npm run build
    
  3. Git Commit:

    git add manifest.json versions.json
    git commit -m "Release v1.0.1"
    git tag v1.0.1
    
  4. GitHub Release:

    • Erstelle Release mit Tag v1.0.1 (ohne v Prefix)
    • Upload main.js, manifest.json, optional styles.css

Code-Standards

TypeScript

  • Strict Mode: Aktiviert
  • No Implicit Any: Aktiviert
  • Strict Null Checks: Aktiviert
  • Module Resolution: Node

Code-Organisation

  • Ein Datei = Ein Verantwortungsbereich
  • Klare Module-Grenzen
  • Keine zirkulären Dependencies
  • Tests neben Source-Code

Naming Conventions

  • Dateien: camelCase.ts (z.B. chainInspector.ts)
  • Klassen: PascalCase (z.B. ChainInspector)
  • Funktionen: camelCase (z.B. executeInspectChains)
  • Interfaces: PascalCase (z.B. MindnetSettings)
  • Types: PascalCase (z.B. ChainRolesConfig)

Kommentare

  • JSDoc für öffentliche APIs
  • Inline-Kommentare für komplexe Logik
  • TODO-Kommentare für geplante Features

Debugging

Console-Logging

console.log("[Module] Message", data);
console.warn("[Module] Warning", data);
console.error("[Module] Error", data);

Debug-Settings

Settings: debugLogging: boolean

Verwendung:

if (this.settings.debugLogging) {
  console.log("[Module] Debug info", data);
}

DevTools

Öffnen: Ctrl+Shift+I (Windows/Linux) oder Cmd+Option+I (Mac)

Console: Für Logs und Errors Network: Für API-Calls (falls vorhanden) Sources: Für Source Maps (Development Build)


Bekannte Einschränkungen

Obsidian API

  • Mobile: Nicht alle APIs verfügbar
  • Desktop-only: isDesktopOnly: true im Manifest
  • CodeMirror: Abhängig von Obsidian-Version

Performance

  • Graph Building: Kann bei großen Vaults langsam sein
  • Live-Reload: Debounced (200ms) für Performance
  • Template Matching: BFS-Limit (max 30 Nodes)

Weitere Ressourcen

  • Benutzerhandbuch: Endnutzer-Workflows
  • Administratorhandbuch: Konfiguration und Wartung
  • Architektur-Dokumentation: System-Übersicht
  • Installation & Deployment: Setup-Anleitung
  • Obsidian API Docs: https://docs.obsidian.md

Ende des Entwicklerhandbuchs