4.2 KiB
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
typefor 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 classfor 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
strictNullChecksenabled - handle null/undefined- Union types for state:
"stopped" | "starting" | "running" | "error" async/awaitover Promises- Explicit return types on public methods
getProcessState(): ProcessState {
return this.processManager?.getState() ?? "stopped";
}
Error Handling
- try/catch for async operations
console.error()for debuggingnew 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
Pluginwithonload()/onunload()lifecycle - Extend
ItemViewfor views:getViewType(),onOpen(),onClose() - Extend
PluginSettingTabfor settings:display() - DOM helpers:
createEl(),createDiv(),setIcon() - Register in
onload(), clean up inonunload()
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.