From ce4fc8c2092d1b0828da5ed62741353975ab71d9 Mon Sep 17 00:00:00 2001 From: Steph Ango <10565871+kepano@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:28:25 -0700 Subject: [PATCH 1/5] First pass --- AGENTS.md | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2fbe85c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,248 @@ +# Obsidian community plugin + +## Project overview + +- Target: Obsidian Community Plugin (TypeScript → bundled JavaScript). +- Entry point: `main.ts` compiled to `main.js` and loaded by Obsidian. +- Required release artifacts: `main.js`, `manifest.json`, and optional `styles.css`. + +## Environment & tooling + +- Node.js: use current LTS (Node 18+ recommended). +- Package manager: npm. +- Bundler: esbuild (preferred). Rollup or webpack are acceptable if they bundle all external dependencies into `main.js`. +- Types: `obsidian` type definitions. + + +### Install + +```bash +npm install +``` + +### Dev (watch) + +```bash +npm run dev +``` + +### Production build + +```bash +npm run build +``` + +## Linting + +- To use eslint install eslint from terminal: `npm install -g eslint` +- To use eslint to analyze this project use this command: `eslint main.ts` +- eslint will then create a report with suggestions for code improvement by file and line number. +- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: `eslint .\src\` + +## File & folder conventions + +- **Organize code into multiple files**: Split functionality across separate modules rather than putting everything in `main.ts`. +- Source lives in `src/`. Keep `main.ts` small and focused on plugin lifecycle (loading, unloading, registering commands). +- **Recommended file structure**: + ``` + src/ + main.ts # Plugin entry point, lifecycle management + settings.ts # Settings interface and defaults + commands/ # Command implementations + command1.ts + command2.ts + ui/ # UI components, modals, views + modal.ts + view.ts + utils/ # Utility functions, helpers + helpers.ts + constants.ts + types.ts # TypeScript interfaces and types + ``` +- Do not commit `node_modules/` or build artifacts. +- Keep the plugin small. Avoid large dependencies. Prefer browser-compatible packages. +- Generated output should be placed at the plugin root or `dist/` depending on your build setup. Release artifacts must end up at the top level of the plugin folder in the vault (`main.js`, `manifest.json`, `styles.css`). + +## Manifest rules (`manifest.json`) + +- Must include (non-exhaustive): + - `id` (plugin ID; for local dev it should match the folder name) + - `name` + - `version` (Semantic Versioning `x.y.z`) + - `minAppVersion` + - `description` + - `isDesktopOnly` (boolean) + - Optional: `author`, `authorUrl`, `fundingUrl` (string or map) +- Never change `id` after release. Treat it as stable API. +- Keep `minAppVersion` accurate when using newer APIs. + +## Building & running in Obsidian + +- Manual install for testing: copy `main.js`, `manifest.json`, `styles.css` (if any) to: + ``` + /.obsidian/plugins// + ``` +- Reload Obsidian and enable the plugin in **Settings → Community plugins**. + +## Commands & settings + +- Any user-facing commands should be added via `this.addCommand(...)`. +- If the plugin has configuration, provide a settings tab and sensible defaults. +- Persist settings using `this.loadData()` / `this.saveData()`. +- Use stable command IDs; avoid renaming once released. + +## Versioning & releases + +- Bump `version` in `manifest.json` (SemVer) and update `versions.json` to map plugin version → minimum app version. +- Create a GitHub release whose tag exactly matches `manifest.json`'s `version` (avoid a leading `v` unless your repo uses it consistently). +- Attach `manifest.json`, `main.js`, and `styles.css` (if present) to the release as individual assets. +- After the initial release, follow the process to add/update your plugin in the community catalog as required. + +## Security, privacy, and compliance + +Follow Obsidian's **Developer Policies** and **Plugin Guidelines**. In particular: + +- Default to local/offline operation. Only make network requests when essential to the feature. +- No hidden telemetry. If you collect optional analytics or call third-party services, require explicit opt-in and document clearly in `README.md` and in settings. +- Never execute remote code, fetch and eval scripts, or auto-update plugin code outside of normal releases. +- Minimize scope: read/write only what's necessary inside the vault. Do not access files outside the vault. +- Clearly disclose any external services used, data sent, and risks. +- Respect user privacy. Do not collect vault contents, filenames, or personal information unless absolutely necessary and explicitly consented. +- Avoid deceptive patterns, ads, or spammy notifications. +- Register and clean up all DOM, app, and interval listeners using the provided `register*` helpers so the plugin unloads safely. + +## UX & copy guidelines (for UI text, commands, settings) + +- Prefer sentence case for headings, buttons, and titles. +- Use clear, action-oriented imperatives in step-by-step copy. +- Use **bold** to indicate literal UI labels. Prefer "select" for interactions. +- Use arrow notation for navigation: **Settings → Community plugins**. +- Keep in-app strings short, consistent, and free of jargon. + +## Performance + +- Keep startup light. Defer heavy work until needed. +- Avoid long-running tasks during `onload`; use lazy initialization. +- Batch disk access and avoid excessive vault scans. +- Debounce/throttle expensive operations in response to file system events. + +## Coding conventions + +- TypeScript with `"strict": true` preferred. +- **Keep `main.ts` minimal**: Focus only on plugin lifecycle (onload, onunload, addCommand calls). Delegate all feature logic to separate modules. +- **Split large files**: If any file exceeds ~200-300 lines, consider breaking it into smaller, focused modules. +- **Use clear module boundaries**: Each file should have a single, well-defined responsibility. +- Bundle everything into `main.js` (no unbundled runtime deps). +- Avoid Node/Electron APIs if you want mobile compatibility; set `isDesktopOnly` accordingly. +- Prefer `async/await` over promise chains; handle errors gracefully. + +## Mobile + +- Where feasible, test on iOS and Android. +- Don't assume desktop-only behavior unless `isDesktopOnly` is `true`. +- Avoid large in-memory structures; be mindful of memory and storage constraints. + +## Agent do/don't + +**Do** +- Add commands with stable IDs (don't rename once released). +- Provide defaults and validation in settings. +- Write idempotent code paths so reload/unload doesn't leak listeners or intervals. +- Use `this.register*` helpers for everything that needs cleanup. + +**Don't** +- Introduce network calls without an obvious user-facing reason and documentation. +- Ship features that require cloud services without clear disclosure and explicit opt-in. +- Store or transmit vault contents unless essential and consented. + +## Common tasks + +### Organize code across multiple files + +**main.ts** (minimal, lifecycle only): +```ts +import { Plugin } from "obsidian"; +import { MySettings, DEFAULT_SETTINGS } from "./settings"; +import { registerCommands } from "./commands"; + +export default class MyPlugin extends Plugin { + settings: MySettings; + + async onload() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + registerCommands(this); + } +} +``` + +**settings.ts**: +```ts +export interface MySettings { + enabled: boolean; + apiKey: string; +} + +export const DEFAULT_SETTINGS: MySettings = { + enabled: true, + apiKey: "", +}; +``` + +**commands/index.ts**: +```ts +import { Plugin } from "obsidian"; +import { doSomething } from "./my-command"; + +export function registerCommands(plugin: Plugin) { + plugin.addCommand({ + id: "do-something", + name: "Do something", + callback: () => doSomething(plugin), + }); +} +``` + +### Add a command + +```ts +this.addCommand({ + id: "your-command-id", + name: "Do the thing", + callback: () => this.doTheThing(), +}); +``` + +### Persist settings + +```ts +interface MySettings { enabled: boolean } +const DEFAULT_SETTINGS: MySettings = { enabled: true }; + +async onload() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + await this.saveData(this.settings); +} +``` + +### Register listeners safely + +```ts +this.registerEvent(this.app.workspace.on("file-open", f => { /* ... */ })); +this.registerDomEvent(window, "resize", () => { /* ... */ }); +this.registerInterval(window.setInterval(() => { /* ... */ }, 1000)); +``` + +## Troubleshooting + +- Plugin doesn't load after build: ensure `main.js` and `manifest.json` are at the top level of the plugin folder under `/.obsidian/plugins//`. +- Commands not appearing: verify `addCommand` runs after `onload` and IDs are unique. +- Settings not persisting: ensure `loadData`/`saveData` are awaited and you re-render the UI after changes. +- Mobile-only issues: confirm you're not using desktop-only APIs; check `isDesktopOnly` and adjust. + +## References + +- Obsidian sample plugin: https://github.com/obsidianmd/obsidian-sample-plugin +- API documentation: https://docs.obsidian.md +- Developer policies: https://docs.obsidian.md/Developer+policies +- Plugin guidelines: https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines +- Style guide: https://help.obsidian.md/style-guide From a4398b8ecc75e738b2d8174e40c97dec35a6e951 Mon Sep 17 00:00:00 2001 From: Steph Ango <10565871+kepano@users.noreply.github.com> Date: Fri, 5 Sep 2025 08:58:49 -0700 Subject: [PATCH 2/5] Corrections based on feedback --- AGENTS.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2fbe85c..8ed7e10 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,10 +9,12 @@ ## Environment & tooling - Node.js: use current LTS (Node 18+ recommended). -- Package manager: npm. -- Bundler: esbuild (preferred). Rollup or webpack are acceptable if they bundle all external dependencies into `main.js`. +- **Package manager: npm** (required for this sample - `package.json` defines npm scripts and dependencies). +- **Bundler: esbuild** (required for this sample - `esbuild.config.mjs` and build scripts depend on it). Alternative bundlers like Rollup or webpack are acceptable for other projects if they bundle all external dependencies into `main.js`. - Types: `obsidian` type definitions. +**Note**: This sample project has specific technical dependencies on npm and esbuild. If you're creating a plugin from scratch, you could choose different tools, but you'll need to replace the build configuration accordingly. + ### Install @@ -59,7 +61,7 @@ npm run build constants.ts types.ts # TypeScript interfaces and types ``` -- Do not commit `node_modules/` or build artifacts. +- **Do not commit build artifacts**: Never commit `node_modules/`, `main.js`, or other generated files to version control. - Keep the plugin small. Avoid large dependencies. Prefer browser-compatible packages. - Generated output should be placed at the plugin root or `dist/` depending on your build setup. Release artifacts must end up at the top level of the plugin folder in the vault (`main.js`, `manifest.json`, `styles.css`). @@ -94,7 +96,7 @@ npm run build ## Versioning & releases - Bump `version` in `manifest.json` (SemVer) and update `versions.json` to map plugin version → minimum app version. -- Create a GitHub release whose tag exactly matches `manifest.json`'s `version` (avoid a leading `v` unless your repo uses it consistently). +- Create a GitHub release whose tag exactly matches `manifest.json`'s `version`. Do not use a leading `v`. - Attach `manifest.json`, `main.js`, and `styles.css` (if present) to the release as individual assets. - After the initial release, follow the process to add/update your plugin in the community catalog as required. @@ -234,7 +236,8 @@ this.registerInterval(window.setInterval(() => { /* ... */ }, 1000)); ## Troubleshooting -- Plugin doesn't load after build: ensure `main.js` and `manifest.json` are at the top level of the plugin folder under `/.obsidian/plugins//`. +- Plugin doesn't load after build: ensure `main.js` and `manifest.json` are at the top level of the plugin folder under `/.obsidian/plugins//`. +- Build issues: if `main.js` is missing, run `npm run build` or `npm run dev` to compile your TypeScript source code. - Commands not appearing: verify `addCommand` runs after `onload` and IDs are unique. - Settings not persisting: ensure `loadData`/`saveData` are awaited and you re-render the UI after changes. - Mobile-only issues: confirm you're not using desktop-only APIs; check `isDesktopOnly` and adjust. From 188bb6120f9c201402490384e812116a0427261b Mon Sep 17 00:00:00 2001 From: Steph Ango <10565871+kepano@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:01:58 -0700 Subject: [PATCH 3/5] small copy tweaks --- AGENTS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8ed7e10..04fa1f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,7 +45,7 @@ npm run build - **Organize code into multiple files**: Split functionality across separate modules rather than putting everything in `main.ts`. - Source lives in `src/`. Keep `main.ts` small and focused on plugin lifecycle (loading, unloading, registering commands). -- **Recommended file structure**: +- **Example file structure**: ``` src/ main.ts # Plugin entry point, lifecycle management @@ -77,8 +77,9 @@ npm run build - Optional: `author`, `authorUrl`, `fundingUrl` (string or map) - Never change `id` after release. Treat it as stable API. - Keep `minAppVersion` accurate when using newer APIs. +- Canonical requirements are coded here: https://github.com/obsidianmd/obsidian-releases/blob/master/.github/workflows/validate-plugin-entry.yml -## Building & running in Obsidian +## Testing - Manual install for testing: copy `main.js`, `manifest.json`, `styles.css` (if any) to: ``` From 33075ecd13f9843f53adebd1b003f08f7c70ab17 Mon Sep 17 00:00:00 2001 From: Steph Ango <10565871+kepano@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:04:04 -0700 Subject: [PATCH 4/5] use forward slashes for cross OS compatibility --- AGENTS.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 04fa1f1..689e160 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,7 +39,7 @@ npm run build - To use eslint install eslint from terminal: `npm install -g eslint` - To use eslint to analyze this project use this command: `eslint main.ts` - eslint will then create a report with suggestions for code improvement by file and line number. -- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: `eslint .\src\` +- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: `eslint ./src/` ## File & folder conventions diff --git a/README.md b/README.md index c773152..23d9526 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Quick starting guide for new plugin devs: - `eslint main.ts` - eslint will then create a report with suggestions for code improvement by file and line number. - If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: - - `eslint .\src\` + - `eslint ./src/` ## Funding URL From 9673533aa976cddf46122214b264bcf1236e1b6c Mon Sep 17 00:00:00 2001 From: Steph Ango <10565871+kepano@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:10:47 -0700 Subject: [PATCH 5/5] language --- AGENTS.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 689e160..3f4274a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,8 +13,7 @@ - **Bundler: esbuild** (required for this sample - `esbuild.config.mjs` and build scripts depend on it). Alternative bundlers like Rollup or webpack are acceptable for other projects if they bundle all external dependencies into `main.js`. - Types: `obsidian` type definitions. -**Note**: This sample project has specific technical dependencies on npm and esbuild. If you're creating a plugin from scratch, you could choose different tools, but you'll need to replace the build configuration accordingly. - +**Note**: This sample project has specific technical dependencies on npm and esbuild. If you're creating a plugin from scratch, you can choose different tools, but you'll need to replace the build configuration accordingly. ### Install