Files
opencode-obsidian/AGENTS.md
Mateusz Tymek d2c90fbd49 Initial commit
2026-01-03 16:08:11 +00:00

4.2 KiB

AGENTS.md - Obsidian OpenCode Plugin

Guidelines for AI coding agents working on the obsidian-opencode plugin.

Project Overview

Obsidian plugin that embeds the OpenCode AI assistant via an iframe. Spawns a local server process and displays its web UI in the Obsidian sidebar.

Tech Stack: TypeScript, Obsidian Plugin API, esbuild, Node.js child processes

Build Commands

bun install          # Install dependencies
bun run dev          # Development (watch mode)
bun run build        # Production (type-check + bundle)

Output: main.js (CommonJS bundle)

Testing & Linting

Not configured. If adding:

  • Tests: Vitest, files in src/__tests__/ or *.test.ts
  • Linting: ESLint + @typescript-eslint/parser
  • Scripts: "test": "vitest run", "lint": "eslint src"

Project Structure

src/
├── main.ts           # Plugin entry, extends Plugin
├── types.ts          # Types and constants
├── OpenCodeView.ts   # Sidebar view (ItemView) with iframe
├── ProcessManager.ts # Server process lifecycle
└── SettingsTab.ts    # Settings UI (PluginSettingTab)

Code Style

Imports

  • ES modules with named imports
  • Order: Obsidian API, Node.js builtins, local modules
  • Use type for type-only imports
  • Relative paths with ./ prefix
import { Plugin, WorkspaceLeaf, Notice } from "obsidian";
import { spawn, ChildProcess } from "child_process";
import type OpenCodePlugin from "./main";
import { OpenCodeSettings, DEFAULT_SETTINGS } from "./types";

Exports

  • export default class for main plugin
  • Named exports for other classes/types/constants
  • One class per file, filename matches class (PascalCase)

Naming Conventions

Type Convention Example
Classes PascalCase OpenCodePlugin, ProcessManager
Interfaces/Types PascalCase OpenCodeSettings, ProcessState
Constants UPPER_CASE or camelCase DEFAULT_SETTINGS, OPENCODE_VIEW_TYPE
Variables/functions camelCase getVaultPath, startServer
Private members camelCase (no prefix) private processManager
Files PascalCase (classes), lowercase (entry) ProcessManager.ts, main.ts

TypeScript Patterns

  • strictNullChecks enabled - handle null/undefined
  • Union types for state: "stopped" | "starting" | "running" | "error"
  • async/await over Promises
  • Explicit return types on public methods
getProcessState(): ProcessState {
  return this.processManager?.getState() ?? "stopped";
}

Error Handling

  • try/catch for async operations
  • console.error() for debugging
  • new Notice() for user-facing errors
  • Boolean returns for success/failure
  • Silent catch for non-critical ops (health checks)
try {
  await this.processManager.start();
} catch (error) {
  console.error("Failed to start:", error);
  new Notice(`Failed to start OpenCode: ${error.message}`);
  return false;
}

Obsidian API Patterns

  • Extend Plugin with onload()/onunload() lifecycle
  • Extend ItemView for views: getViewType(), onOpen(), onClose()
  • Extend PluginSettingTab for settings: display()
  • DOM helpers: createEl(), createDiv(), setIcon()
  • Register in onload(), clean up in onunload()
this.registerView(OPENCODE_VIEW_TYPE, (leaf) => new OpenCodeView(leaf, this));
this.addCommand({ id: "toggle-view", name: "Toggle panel", callback: () => this.toggleView() });

DOM Creation

const container = this.contentEl.createDiv({ cls: "opencode-container" });
container.createEl("h3", { text: "Title" });
container.createEl("button", { text: "Click", cls: "mod-cta" });

State Management

  • Callback-based subscriptions
  • Centralized state in manager classes
  • Immediate notification on state change

Config Summary

tsconfig.json: ES6 target, ESNext modules, strictNullChecks, noImplicitAny

esbuild: CJS format, es2018 target, node platform. Externals: obsidian, electron, CodeMirror, Node builtins

Desktop-Only

Uses Node.js APIs unavailable on mobile:

  • child_process.spawn() for server process
  • File system via vault adapter

Check for desktop environment before adding mobile-incompatible features.