{"id":51121578,"url":"https://github.com/josegcpa/elf-obsidian-plugin","last_synced_at":"2026-06-25T03:01:15.424Z","repository":{"id":360906304,"uuid":"1242298678","full_name":"josegcpa/elf-obsidian-plugin","owner":"josegcpa","description":"Elf is an Obsidian plugin that brings AI writing assistance into your vault.","archived":false,"fork":false,"pushed_at":"2026-05-28T12:07:59.000Z","size":168,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T12:08:13.058Z","etag":null,"topics":["ai","ai-tools","anthropic","google","mistral","obsidian-plugin","ollama","openai","openrouter","writing"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josegcpa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T09:51:46.000Z","updated_at":"2026-05-28T12:08:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/josegcpa/elf-obsidian-plugin","commit_stats":null,"previous_names":["josegcpa/elf-obsidian-plugin"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/josegcpa/elf-obsidian-plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegcpa%2Felf-obsidian-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegcpa%2Felf-obsidian-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegcpa%2Felf-obsidian-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegcpa%2Felf-obsidian-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josegcpa","download_url":"https://codeload.github.com/josegcpa/elf-obsidian-plugin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegcpa%2Felf-obsidian-plugin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34757355,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-tools","anthropic","google","mistral","obsidian-plugin","ollama","openai","openrouter","writing"],"created_at":"2026-06-25T03:01:14.635Z","updated_at":"2026-06-25T03:01:15.413Z","avatar_url":"https://github.com/josegcpa.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"assets/icon.png\" alt=\"drawing\" width=\"200\"/\u003e\n\u003c/p\u003e\n\n\u003ch1\u003eElf Obsidian plugin\u003c/h1\u003e\n\nElf is an [Obsidian](https://obsidian.md) plugin that brings AI writing assistance into your vault.\nIt supports **six providers** and three distinct **modes of action**.\n\n## Features\n\n- **Collaborate** — the AI continues your current paragraph from the cursor position.\n- **Rewrite** — the AI rewrites selected text in place, using surrounding context so the result fits seamlessly into the document.\n- **Variations** — the AI generates multiple alternative rewrites of the selection; a modal lets you preview, accept, copy all, or regenerate.\n- **Prompt library** — create and edit prompts for each mode, stored in a `prompts.md` file in your vault.\n- **File link resolution** — embed `[[wikilinks]]` in any prompt template; the linked file's contents are appended to the user message automatically.\n- **Dataview integration** — `dataview` and `dataviewjs` code blocks inside linked files are rendered to markdown tables before being sent to the model.\n- **Command palette** commands with default hotkeys.\n- **Right-click context menu** when text is selected.\n\n## Supported Providers\n\n| Provider       | Notes                                                     |\n|----------------|-----------------------------------------------------------|\n| **OpenAI**     | Requires an API key.                                      |\n| **Anthropic**  | Requires an API key.                                      |\n| **Google**     | Requires an API key (Gemini).                             |\n| **Mistral**    | Requires an API key.                                      |\n| **Ollama**     | Local server — no API key needed. Configure the base URL. |\n| **OpenRouter** | Single key that routes to many models.                    |\n\n## Configuration\n\nOpen **Settings → Elf**.\n\n1. Select a **Provider** and enter your **API key** (or Ollama base URL).\n2. Models load automatically — pick from the dropdown or click **Refresh**.\n3. Set **Variations count** (1–10, default 3) — how many alternatives are generated each time.\n4. Under **Default Prompts**, choose which prompt is used for each mode's default command.\n5. Under **Prompt Library**, set the path to your `prompts.md` file and edit prompts inline.\n\n## Commands\n\n| Command                         | Hotkey     | Description                              |\n|---------------------------------|------------|------------------------------------------|\n| Collaborate: continue writing   | `Ctrl/⌘⇧C` | Inserts AI continuation after the cursor |\n| Rewrite: rewrite selection      | `Ctrl/⌘⇧R` | Replaces selection with AI rewrite       |\n| Variations: generate variations | `Ctrl/⌘⇧V` | Opens variations modal                   |\n| Collaborate: pick a prompt…     | —          | Fuzzy-pick a Collaborate prompt          |\n| Rewrite: pick a prompt…         | —          | Fuzzy-pick a Rewrite prompt              |\n| Variations: pick a prompt…      | —          | Fuzzy-pick a Variations prompt           |\n| Select provider and model…      | —          | Switch provider and model                |\n\n### Variations modal\n\n- `↑` / `↓` or mouse — navigate options.\n- `Enter` — accept highlighted variation.\n- `Shift+Enter` — regenerate a new batch.\n- `Ctrl/⌘+A` — copy all variations to the clipboard (numbered list).\n- `Esc` — cancel.\n\n### Right-click menu\n\nRight-click selected text to access **Rewrite with AI**, **Rewrite with AI…**, and **Generate variations…**.\n\n## Prompt Templates\n\nPrompts use `{{placeholder}}` variables:\n\n| Placeholder    | Available in        | Description                                                                                             |\n|----------------|---------------------|---------------------------------------------------------------------------------------------------------|\n| `{{before}}`   | All modes           | Collaborate: paragraph up to the cursor. Rewrite/Variations: up to 500 characters before the selection. |\n| `{{after}}`    | Rewrite, Variations | Up to 500 characters after the selection                                                                |\n| `{{selected}}` | Rewrite, Variations | The currently selected text                                                                             |\n| `{{n}}`        | Variations          | Number of variations to generate                                                                        |\n\n### File links\n\nAny `[[wikilink]]` in a prompt template is resolved before the prompt is sent:\n\n1. The link is replaced inline with `[FILE: path/to/file.md]`.\n2. The file's contents are appended at the end of the user message under `[CONTENTS OF FILE: path/to/file.md]`.\n3. `dataview` / `dataviewjs` blocks inside the linked file are rendered via the Dataview plugin API (if installed).\n4. Wikilinks inside linked files are resolved recursively; circular references are safely deduplicated.\n\n### Default system prompts\n\nEach mode has a default system prompt stored as YAML front-matter in `prompts.md`:\n\n```yaml\n---\ndefault_system_prompt_collaborate: \"...\"\ndefault_system_prompt_rewrite: \"...\"\ndefault_system_prompt_variations: \"...\"\n---\n```\n\nIndividual prompts can override this by including a `### System prompt` section. If the section is absent, the mode default is used.\n\n## Developing\n\n### Setup\n\n```bash\ngit clone \u003crepo\u003e\ncd elf-obsidian-plugin\nnpm install\nnpm run build\n```\n\nCopy the built files into your vault:\n\n```\n\u003cvault\u003e/.obsidian/plugins/elf-obsidian-plugin/\n├── main.js\n├── manifest.json\n└── styles.css\n```\n\nEnable the plugin under **Settings → Community plugins → Installed plugins**.\n\n\u003e Run `npm run dev` for watch mode — esbuild rebuilds `main.js` on every change.\n\n### Project Structure\n\n```\nsrc/\n├── main.ts                    # Plugin entry point\n├── types.ts                   # Shared types and default values\n├── engine.ts                  # Collaborate, Rewrite \u0026 Variations logic\n├── file-resolver.ts           # [[wikilink]] and Dataview block resolution\n├── prompt-file.ts             # Prompt library serialisation / parsing\n├── settings-tab.ts            # Settings UI\n├── variations-modal.ts        # Variations navigation modal\n├── providers/\n│   ├── base.ts                # LLMProvider interface + HTTP helpers\n│   ├── factory.ts             # createProvider() + default models\n│   ├── openai.ts\n│   ├── anthropic.ts\n│   ├── google.ts\n│   ├── mistral.ts\n│   ├── ollama.ts\n│   └── openrouter.ts\n└── tests/\n    ├── __mocks__/\n    │   └── obsidian.ts\n    ├── setup.ts\n    ├── engine.test.ts\n    ├── file-resolver.test.ts\n    ├── prompt-file.test.ts\n    └── providers.test.ts\n```\n\n### Adding a New Provider\n\n1. Create `src/providers/\u003cname\u003e.ts` implementing `LLMProvider`:\n\n   ```ts\n   export class MyProvider implements LLMProvider {\n     async complete(request: LLMRequest): Promise\u003cLLMResponse\u003e { … }\n     async listModels(): Promise\u003cstring[]\u003e { … }\n   }\n   ```\n\n2. Add the value to `ProviderType` in `types.ts`.\n3. Register it in `createProvider()` and `PROVIDER_DEFAULT_MODELS` in `factory.ts`.\n4. Add a label to the `PROVIDERS` array in `settings-tab.ts`.\n\n### Testing\n\n```bash\nnpm test\n```\n\nUnit tests run without API keys. Integration tests are skipped automatically when the relevant key is absent. Copy `.env.example` to `.env` and fill in keys to run them:\n\n```bash\ncp .env.example .env\nnpm test\n```\n\n## License\n\nMIT\n\n## Disclaimer\n\nThis was written, in great part, with recourse to generative AI. In particular, I made use of a mixture of models within Windsurf to make a large part of this happen.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosegcpa%2Felf-obsidian-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosegcpa%2Felf-obsidian-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosegcpa%2Felf-obsidian-plugin/lists"}