Rename plugin to Vaultkeeper AI

This commit is contained in:
Andrew Beal 2025-11-10 08:03:27 +00:00
parent 619d3c423c
commit a217e84643
41 changed files with 261 additions and 261 deletions

View file

@ -7,7 +7,7 @@ import type { Conversation } from "Conversations/Conversation";
import { AIProvider, AIProviderURL } from "Enums/ApiProvider";
import { AIFunctionCall } from "AIClasses/AIFunctionCall";
import type { IAIFunctionDefinition } from "AIClasses/FunctionDefinitions/IAIFunctionDefinition";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import type { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunctionDefinitions";
import { isValidJson } from "Helpers/Helpers";
import type { ConversationContent } from "Conversations/ConversationContent";
@ -20,7 +20,7 @@ export class Claude implements IAIClass {
private readonly apiKey: string;
private readonly aiPrompt: IPrompt = Resolve<IPrompt>(Services.IPrompt);
private readonly plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
private readonly plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
private readonly settingsService: SettingsService = Resolve<SettingsService>(Services.SettingsService);
private readonly streamingService: StreamingService = Resolve<StreamingService>(Services.StreamingService);
private readonly aiFunctionDefinitions: AIFunctionDefinitions = Resolve<AIFunctionDefinitions>(Services.AIFunctionDefinitions);

View file

@ -8,7 +8,7 @@ import { Role } from "Enums/Role";
import { AIProvider, AIProviderURL } from "Enums/ApiProvider";
import { AIFunctionCall } from "AIClasses/AIFunctionCall";
import type { IAIFunctionDefinition } from "AIClasses/FunctionDefinitions/IAIFunctionDefinition";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import type { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunctionDefinitions";
import { isValidJson } from "Helpers/Helpers";
import type { ConversationContent } from "Conversations/ConversationContent";
@ -21,7 +21,7 @@ export class Gemini implements IAIClass {
private readonly apiKey: string;
private readonly aiPrompt: IPrompt = Resolve<IPrompt>(Services.IPrompt);
private readonly plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
private readonly plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
private readonly settingsService: SettingsService = Resolve<SettingsService>(Services.SettingsService);
private readonly streamingService: StreamingService = Resolve<StreamingService>(Services.StreamingService);
private readonly aiFunctionDefinitions: AIFunctionDefinitions = Resolve<AIFunctionDefinitions>(Services.AIFunctionDefinitions);

View file

@ -1,4 +1,4 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Resolve } from "Services/DependencyService";
import { Services } from "Services/Services";
import { SystemInstruction } from "./SystemPrompt";
@ -12,12 +12,12 @@ export interface IPrompt {
export class AIPrompt implements IPrompt {
private readonly plugin: VaultAIPlugin;
private readonly plugin: VaultkeeperAIPlugin;
private readonly settingsService: SettingsService;
private readonly fileSystemService: FileSystemService;
public constructor() {
this.plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
this.settingsService = Resolve<SettingsService>(Services.SettingsService);
this.fileSystemService = Resolve<FileSystemService>(Services.FileSystemService);
}

View file

@ -7,7 +7,7 @@ import type { Conversation } from "Conversations/Conversation";
import { AIProvider, AIProviderURL } from "Enums/ApiProvider";
import { AIFunctionCall } from "AIClasses/AIFunctionCall";
import type { IAIFunctionDefinition } from "AIClasses/FunctionDefinitions/IAIFunctionDefinition";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import type { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunctionDefinitions";
import { Role } from "Enums/Role";
import { isValidJson } from "Helpers/Helpers";
@ -25,7 +25,7 @@ export class OpenAI implements IAIClass {
private readonly apiKey: string;
private readonly aiPrompt: IPrompt = Resolve<IPrompt>(Services.IPrompt);
private readonly plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
private readonly plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
private readonly settingsService: SettingsService = Resolve<SettingsService>(Services.SettingsService);
private readonly streamingService: StreamingService = Resolve<StreamingService>(Services.StreamingService);
private readonly aiFunctionDefinitions: AIFunctionDefinitions = Resolve<AIFunctionDefinitions>(Services.AIFunctionDefinitions);

View file

@ -6,7 +6,7 @@
import { tick, onMount } from "svelte";
import { conversationStore } from "../Stores/ConversationStore";
import { Conversation } from "Conversations/Conversation";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { openPluginSettings } from "Helpers/Helpers";
import { Selector } from "Enums/Selector";
import type { WorkSpaceService } from "Services/WorkSpaceService";
@ -14,7 +14,7 @@
import type { ConversationFileSystemService } from "Services/ConversationFileSystemService";
import type { SettingsService } from "Services/SettingsService";
const plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const settingsService: SettingsService = Resolve<SettingsService>(Services.SettingsService);
const chatService: ChatService = Resolve<ChatService>(Services.ChatService);
const workSpaceService: WorkSpaceService = Resolve<WorkSpaceService>(Services.WorkSpaceService);

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { Resolve } from "../Services/DependencyService";
import { Services } from "../Services/Services";
import type VaultAIPlugin from "../main";
import type VaultkeeperAIPlugin from "../main";
import { setIcon, type WorkspaceLeaf } from "obsidian";
import { ConversationFileSystemService } from "../Services/ConversationFileSystemService";
import { conversationStore } from "../Stores/ConversationStore";
@ -14,7 +14,7 @@
export let leaf: WorkspaceLeaf;
export let onNewConversation: (() => void) | undefined = undefined;
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const conversationFileSystemService = Resolve<ConversationFileSystemService>(Services.ConversationFileSystemService);
const chatService: ChatService = Resolve<ChatService>(Services.ChatService);
@ -115,7 +115,7 @@
id="settings-button"
class="top-bar-button clickable-icon"
on:click={openSettings}
aria-label="Vault AI Settings"
aria-label="Vaultkeeper AI Settings"
></button>
<button
bind:this={helpMenuButton}
@ -133,7 +133,7 @@
id="close-button"
class="top-bar-button clickable-icon"
on:click={closePlugin}
aria-label="Close Vault AI"
aria-label="Close Vaultkeeper AI"
></button>
</div>
</main>

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { Copy } from "Enums/Copy";
import { Path } from "Enums/Path";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { HelpModal } from "Modals/HelpModal";
import { basename } from "path-browserify";
import { Resolve } from "Services/DependencyService";
@ -12,7 +12,7 @@
export let userInstructionActive: boolean;
const plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const settingsService: SettingsService = Resolve<SettingsService>(Services.SettingsService);
const fileSystemService: FileSystemService = Resolve<FileSystemService>(Services.FileSystemService);

View file

@ -58,14 +58,14 @@ export enum Copy {
// Help Modal Copy
HelpModalAboutTitle = "About",
HelpModalAboutContent = `#### About Vault AI
HelpModalAboutContent = `#### About Vaultkeeper AI
This plugin was originally created for a friend who found it useful, so I have decided to release it to the Obsidian community.
If you find any issues, please feel free to raise them on GitHub:`,
HelpModalGuideTitle = "Plugin Guide",
HelpModalGuideContent = `#### How to Use Vault AI
HelpModalGuideContent = `#### How to Use Vaultkeeper AI
##### Getting Started
1. **Add an API Key**: Go to Settings and add at least one API key (Claude, Gemini, or OpenAI)
@ -100,7 +100,7 @@ The autocomplete dropdown supports keyboard navigation.
Customize AI behavior for specific workflows:
1. Create markdown files in **Vault AI/User Instructions/**
1. Create markdown files in **Vaultkeeper AI/User Instructions/**
2. Click the "User Instructions" button in chat
3. Select your instruction set
4. The AI follows these instructions for all interactions
@ -153,9 +153,9 @@ This error means you've made too many API requests in a given time period. This
**What's stored locally**:
- API keys (stored in your vault's plugin settings)
- Conversation history (stored in \`Vault AI/Conversations/\`)
- Custom instructions (stored in \`Vault AI/User Instructions/\`)
- Plugin settings (stored in \`.obsidian/plugins/vault-ai/\`)
- Conversation history (stored in \`Vaultkeeper AI/Conversations/\`)
- Custom instructions (stored in \`Vaultkeeper AI/User Instructions/\`)
- Plugin settings (stored in \`.obsidian/plugins/vaultkeeper-ai/\`)
##### API Communication
@ -213,7 +213,7 @@ Each AI provider has their own data policies:
CoffeeLinkText = "Buy me a coffee",
// Thank You Message
ThankYouMessage = "Thanks for using the Vault AI plugin!",
ThankYouMessage = "Thanks for using the Vaultkeeper AI plugin!",
// SVG Icons
GitHubIconPath = "M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z",

View file

@ -1,7 +1,7 @@
export enum Path {
Root = "/",
VaultAIDir = "Vault AI",
Conversations = `${Path.VaultAIDir}/Conversations`,
UserInstructions = `${Path.VaultAIDir}/User Instructions`,
VaultkeeperAIDir = "Vaultkeeper AI",
Conversations = `${Path.VaultkeeperAIDir}/Conversations`,
UserInstructions = `${Path.VaultkeeperAIDir}/User Instructions`,
ExampleUserInstructions = `${Path.UserInstructions}/EXAMPLE_INSTRUCTIONS.md`
};

View file

@ -1,5 +1,5 @@
export enum Selector {
MarkDownLink = "vault-ai-internal-markdown-link",
MarkDownLink = "vaultkeeper-ai-internal-markdown-link",
AIExclusionsInput = "ai-exclusions-input",
ApiKeySettingOk = "api-key-setting-ok",
ApiKeySettingError = "api-key-setting-error",

View file

@ -1,6 +1,6 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
export function openPluginSettings(plugin: VaultAIPlugin) {
export function openPluginSettings(plugin: VaultkeeperAIPlugin) {
// @ts-ignore - accessing internal API
plugin.app.setting.open();
// @ts-ignore - accessing internal API

View file

@ -10,7 +10,7 @@ import { dateToString } from 'Helpers/Helpers';
import { conversationStore } from 'Stores/ConversationStore';
import { Selector } from 'Enums/Selector';
import type { ChatService } from 'Services/ChatService';
import type VaultAIPlugin from 'main';
import type VaultkeeperAIPlugin from 'main';
interface IListItem {
id: string;
@ -33,7 +33,7 @@ export class ConversationHistoryModal extends Modal {
public onModalClose?: () => void;
constructor() {
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
super(plugin.app);
}

View file

@ -1,4 +1,4 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Modal } from "obsidian";
import { Resolve } from "Services/DependencyService";
import { Services } from "Services/Services";
@ -11,7 +11,7 @@ export class HelpModal extends Modal {
private component: Record<string, any> | null = null;
public constructor() {
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
super(plugin.app);
}

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { Copy } from "Enums/Copy";
import { Selector } from "Enums/Selector";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { DropdownComponent, setIcon } from "obsidian";
import { Resolve } from "Services/DependencyService";
import { Services } from "Services/Services";
@ -13,7 +13,7 @@
export let onClose: () => void;
export let initialTopic: number = 1;
const plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const streamingMarkdownService: StreamingMarkdownService = Resolve<StreamingMarkdownService>(Services.StreamingMarkdownService);
const workSpaceService: WorkSpaceService = Resolve<WorkSpaceService>(Services.WorkSpaceService);

View file

@ -1,4 +1,4 @@
# Vault AI for Obsidian
# Vaultkeeper AI for Obsidian
> A powerful AI assistant plugin that brings Claude, Gemini, and OpenAI directly into your Obsidian vault with intelligent note management capabilities.
@ -28,16 +28,16 @@
### Manual Installation
1. Download the latest release from the [Releases page](https://github.com/andy-stack/vault-ai/releases)
2. Extract the files into your vault's `.obsidian/plugins/vault-ai/` directory
1. Download the latest release from the [Releases page](https://github.com/andy-stack/vaultkeeper-ai/releases)
2. Extract the files into your vault's `.obsidian/plugins/vaultkeeper-ai/` directory
3. Reload Obsidian
4. Enable "Vault AI" in Settings → Community Plugins
4. Enable "Vaultkeeper AI" in Settings → Community Plugins
### From Community Plugins
1. Open Obsidian Settings
2. Navigate to Community Plugins
3. Search for "Vault AI"
3. Search for "Vaultkeeper AI"
4. Click Install, then Enable
## Quick Start
@ -49,7 +49,7 @@
2. **Select a Model**: Choose your preferred AI model from the dropdown
3. **Start Chatting**: Click the Vault AI icon in the left sidebar to open the chat window
3. **Start Chatting**: Click the Vaultkeeper AI icon in the left sidebar to open the chat window
4. **Try the Reference System**:
- Type `@` to reference files
@ -130,7 +130,7 @@ The autocomplete dropdown appears automatically and supports:
Customize the AI's behavior with system instructions:
1. Create markdown files in `Vault AI/User Instructions/`
1. Create markdown files in `Vaultkeeper AI/User Instructions/`
2. Click the "User Instructions" button in the chat
3. Select your custom instruction set
4. The AI will follow these instructions for all interactions
@ -149,7 +149,7 @@ See `EXAMPLE_INSTRUCTIONS.md` in your vault for a template.
- All conversations are automatically saved
- Click the history icon to browse past conversations
- Conversations are automatically named by AI based on content
- Stored in `Vault AI/Conversations/` as JSON files
- Stored in `Vaultkeeper AI/Conversations/` as JSON files
## Configuration
@ -205,7 +205,7 @@ Protect your privacy by preventing the AI from accessing sensitive files or dire
**Custom Instructions Path**
- Customize where instruction files are stored
- Default: `Vault AI/User Instructions/`
- Default: `Vaultkeeper AI/User Instructions/`
## Development
@ -220,8 +220,8 @@ Protect your privacy by preventing the AI from accessing sensitive files or dire
```bash
# Clone into your vault's plugin directory
cd /path/to/vault/.obsidian/plugins/
git clone https://github.com/andy-stack/vault-ai.git
cd vault-ai
git clone https://github.com/andy-stack/vaultkeeper-ai.git
cd vaultkeeper-ai
# Install dependencies
npm install
@ -280,7 +280,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
## Support
- **Issues**: Report bugs or request features on [GitHub Issues](https://github.com/andy-stack/vault-ai/issues)
- **Issues**: Report bugs or request features on [GitHub Issues](https://github.com/andy-stack/vaultkeeper-ai/issues)
## Acknowledgments

View file

@ -1,4 +1,4 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { TAbstractFile, TFile, TFolder, type Vault } from "obsidian";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";

View file

@ -1,5 +1,5 @@
import { AIProvider } from "Enums/ApiProvider";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { RegisterSingleton, RegisterTransient, Resolve } from "./DependencyService";
import { Services } from "./Services";
import { AIPrompt, type IPrompt } from "AIClasses/IPrompt";
@ -37,13 +37,13 @@ import { HTMLService } from "./HTMLService";
import { SettingsService } from "./SettingsService";
import { HelpModal } from "Modals/HelpModal";
export async function RegisterPlugin(plugin: VaultAIPlugin) {
RegisterSingleton<VaultAIPlugin>(Services.VaultAIPlugin, plugin);
export async function RegisterPlugin(plugin: VaultkeeperAIPlugin) {
RegisterSingleton<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin, plugin);
RegisterSingleton<SettingsService>(Services.SettingsService, new SettingsService(await plugin.loadData()));
}
export function RegisterDependencies() {
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
RegisterSingleton<FileManager>(Services.FileManager, plugin.app.fileManager);
RegisterSingleton<StatusBarService>(Services.StatusBarService, new StatusBarService());

View file

@ -1,5 +1,5 @@
export class Services {
static VaultAIPlugin = Symbol("VaultAIPlugin");
static VaultkeeperAIPlugin = Symbol("VaultkeeperAIPlugin");
static SettingsService = Symbol("SettingsService");
static StatusBarService = Symbol("StatusBarService");
static HTMLService = Symbol("HTMLService");

View file

@ -1,9 +1,9 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";
import { AIProvider, AIProviderModel } from "Enums/ApiProvider";
const DEFAULT_SETTINGS: IVaultAISettings = {
const DEFAULT_SETTINGS: IVaultkeeperAISettings = {
firstTimeStart: true,
userInstruction: "",
@ -19,7 +19,7 @@ const DEFAULT_SETTINGS: IVaultAISettings = {
snippetSizeLimit: 300
}
export interface IVaultAISettings {
export interface IVaultkeeperAISettings {
firstTimeStart: boolean;
userInstruction: string;
@ -37,12 +37,12 @@ export interface IVaultAISettings {
export class SettingsService {
private readonly plugin: VaultAIPlugin;
private readonly plugin: VaultkeeperAIPlugin;
public readonly settings: IVaultAISettings;
public readonly settings: IVaultkeeperAISettings;
public constructor(loadedSettings: Partial<IVaultAISettings>) {
this.plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
public constructor(loadedSettings: Partial<IVaultkeeperAISettings>) {
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
this.settings = Object.assign({}, DEFAULT_SETTINGS, loadedSettings);
}

View file

@ -1,18 +1,18 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";
import { Selector } from "Enums/Selector";
export class StatusBarService {
private readonly plugin: VaultAIPlugin;
private readonly plugin: VaultkeeperAIPlugin;
private statusBarItem: HTMLElement | null;
private currentInputTokens: number = 0;
private currentOutputTokens: number = 0;
private animationFrame: number | null = null;
public constructor() {
this.plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
}
public setStatusBarMessage(message: string) {

View file

@ -1,4 +1,4 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";
import type { VaultService } from "./VaultService";
@ -17,7 +17,7 @@ export class VaultCacheService {
key: "prepared"
};
private readonly plugin: VaultAIPlugin;
private readonly plugin: VaultkeeperAIPlugin;
private readonly vaultService: VaultService;
private readonly metaDataCache: MetadataCache;
@ -33,7 +33,7 @@ export class VaultCacheService {
private initialised = false;
public constructor() {
this.plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
this.vaultService = Resolve<VaultService>(Services.VaultService);
this.metaDataCache = this.plugin.app.metadataCache;
this.registerFileEvents();

View file

@ -1,7 +1,7 @@
import { FileManager, TAbstractFile, TFile, TFolder, type Vault } from "obsidian";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Path } from "Enums/Path";
import { escapeRegex, randomSample } from "Helpers/Helpers";
import type { ISearchMatch, ISearchSnippet } from "../Helpers/SearchTypes";
@ -16,17 +16,17 @@ interface IFileEventArgs {
/* This service protects the users vault through their exclusions. The plugin root is excluded by default */
export class VaultService {
private readonly AGENT_ROOT_DIR = Path.VaultAIDir;
private readonly AGENT_ROOT_CONTENTS = `${Path.VaultAIDir}/**`;
private readonly AGENT_ROOT_DIR = Path.VaultkeeperAIDir;
private readonly AGENT_ROOT_CONTENTS = `${Path.VaultkeeperAIDir}/**`;
private readonly vault: Vault;
private readonly plugin: VaultAIPlugin;
private readonly plugin: VaultkeeperAIPlugin;
private readonly settingsService: SettingsService;
private readonly fileManager: FileManager;
private readonly sanitiserService: SanitiserService;
public constructor() {
this.plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
this.vault = this.plugin.app.vault;
this.settingsService = Resolve<SettingsService>(Services.SettingsService);
this.fileManager = Resolve<FileManager>(Services.FileManager);

View file

@ -1,10 +1,10 @@
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";
import type { TFile, WorkspaceLeaf } from "obsidian";
export class WorkSpaceService {
private readonly plugin: VaultAIPlugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
private readonly plugin: VaultkeeperAIPlugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
public async openNote(noteName: string) {
const file: TFile | null = this.plugin.app.metadataCache.getFirstLinkpathDest(noteName, "");

View file

@ -1,21 +1,21 @@
import { AIProvider, AIProviderModel } from "Enums/ApiProvider";
import { Copy } from "Enums/Copy";
import { Selector } from "Enums/Selector";
import type VaultAIPlugin from "main";
import type VaultkeeperAIPlugin from "main";
import { PluginSettingTab, Setting, setIcon, setTooltip } from "obsidian";
import { Resolve } from "Services/DependencyService";
import type { SettingsService } from "Services/SettingsService";
import { Services } from "Services/Services";
import { RegisterAiProvider } from "Services/ServiceRegistration";
export class VaultAISettingTab extends PluginSettingTab {
export class VaultkeeperAISettingTab extends PluginSettingTab {
private readonly settingsService: SettingsService;
private apiKeySetting: Setting | null = null;
private apiKeyInputEl: HTMLInputElement | null = null;
constructor() {
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
super(plugin.app, plugin);
this.settingsService = Resolve<SettingsService>(Services.SettingsService);
}
@ -134,7 +134,7 @@ export class VaultAISettingTab extends PluginSettingTab {
.onChange(async (value) => {
const provider = AIProvider.fromModel(this.settingsService.settings.model);
this.settingsService.setApiKeyForProvider(provider, value);
await this.settingsService.saveSettings();
await this.settingsService.saveSettings(() => RegisterAiProvider());
this.highlightApiKey();
});
text.inputEl.type = "password";

View file

@ -6,7 +6,7 @@ import type { StatusBarService } from 'Services/StatusBarService';
import { Resolve } from 'Services/DependencyService';
import { Services } from 'Services/Services';
export const VIEW_TYPE_MAIN = 'vault-ai-main-view';
export const VIEW_TYPE_MAIN = 'vaultkeeper-ai-main-view';
export class MainView extends ItemView {
@ -24,7 +24,7 @@ export class MainView extends ItemView {
}
getDisplayText() {
return "Vault AI";
return "Vaultkeeper AI";
}
getIcon(): string {

View file

@ -4,7 +4,7 @@ import { RegisterSingleton, Resolve, DeregisterAllServices } from '../../Service
import { Services } from '../../Services/Services';
import { StreamingService } from '../../Services/StreamingService';
import type { IPrompt } from '../../AIClasses/IPrompt';
import type VaultAIPlugin from '../../main';
import type VaultkeeperAIPlugin from '../../main';
import { AIFunctionDefinitions } from '../../AIClasses/FunctionDefinitions/AIFunctionDefinitions';
import { Conversation } from '../../Conversations/Conversation';
import { ConversationContent } from '../../Conversations/ConversationContent';
@ -28,9 +28,9 @@ describe('Claude', () => {
};
RegisterSingleton(Services.IPrompt, mockPrompt);
// Mock VaultAIPlugin
// Mock VaultkeeperAIPlugin
mockPlugin = {};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
// Mock SettingsService
mockSettingsService = {
@ -95,7 +95,7 @@ describe('Claude', () => {
it('should resolve all required services', () => {
const prompt = Resolve<IPrompt>(Services.IPrompt);
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const settingsService = Resolve<SettingsService>(Services.SettingsService);
const streaming = Resolve<StreamingService>(Services.StreamingService);
const functions = Resolve<AIFunctionDefinitions>(Services.AIFunctionDefinitions);

View file

@ -14,7 +14,7 @@ describe('ClaudeConversationNamingService', () => {
beforeEach(() => {
mockPlugin = {};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
// Mock SettingsService
mockSettingsService = {

View file

@ -4,7 +4,7 @@ import { RegisterSingleton, Resolve, DeregisterAllServices } from '../../Service
import { Services } from '../../Services/Services';
import { StreamingService } from '../../Services/StreamingService';
import type { IPrompt } from '../../AIClasses/IPrompt';
import type VaultAIPlugin from '../../main';
import type VaultkeeperAIPlugin from '../../main';
import { AIFunctionDefinitions } from '../../AIClasses/FunctionDefinitions/AIFunctionDefinitions';
import { Conversation } from '../../Conversations/Conversation';
import { ConversationContent } from '../../Conversations/ConversationContent';
@ -28,9 +28,9 @@ describe('Gemini', () => {
};
RegisterSingleton(Services.IPrompt, mockPrompt);
// Mock VaultAIPlugin
// Mock VaultkeeperAIPlugin
mockPlugin = {};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
// Mock SettingsService
mockSettingsService = {
@ -94,7 +94,7 @@ describe('Gemini', () => {
it('should resolve all required services', () => {
const prompt = Resolve<IPrompt>(Services.IPrompt);
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const settingsService = Resolve<SettingsService>(Services.SettingsService);
const streaming = Resolve<StreamingService>(Services.StreamingService);
const functions = Resolve<AIFunctionDefinitions>(Services.AIFunctionDefinitions);

View file

@ -14,7 +14,7 @@ describe('GeminiConversationNamingService', () => {
beforeEach(() => {
mockPlugin = {};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
// Mock SettingsService
mockSettingsService = {

View file

@ -4,7 +4,7 @@ import { RegisterSingleton, Resolve, DeregisterAllServices } from '../../Service
import { Services } from '../../Services/Services';
import { StreamingService } from '../../Services/StreamingService';
import type { IPrompt } from '../../AIClasses/IPrompt';
import type VaultAIPlugin from '../../main';
import type VaultkeeperAIPlugin from '../../main';
import { AIFunctionDefinitions } from '../../AIClasses/FunctionDefinitions/AIFunctionDefinitions';
import { Conversation } from '../../Conversations/Conversation';
import { ConversationContent } from '../../Conversations/ConversationContent';
@ -28,9 +28,9 @@ describe('OpenAI', () => {
};
RegisterSingleton(Services.IPrompt, mockPrompt);
// Mock VaultAIPlugin
// Mock VaultkeeperAIPlugin
mockPlugin = {};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
// Mock SettingsService
mockSettingsService = {
@ -94,7 +94,7 @@ describe('OpenAI', () => {
it('should resolve all required services', () => {
const prompt = Resolve<IPrompt>(Services.IPrompt);
const plugin = Resolve<VaultAIPlugin>(Services.VaultAIPlugin);
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const settingsService = Resolve<SettingsService>(Services.SettingsService);
const streaming = Resolve<StreamingService>(Services.StreamingService);
const functions = Resolve<AIFunctionDefinitions>(Services.AIFunctionDefinitions);

View file

@ -14,7 +14,7 @@ describe('OpenAIConversationNamingService', () => {
beforeEach(() => {
mockPlugin = {};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
// Mock SettingsService
mockSettingsService = {

View file

@ -80,21 +80,21 @@ describe('ConversationFileSystemService - Integration Tests', () => {
const conversation = createTestConversation('My Test Note');
const path = service.generateConversationPath(conversation);
expect(path).toBe('Vault AI/Conversations/My Test Note.json');
expect(path).toBe('Vaultkeeper AI/Conversations/My Test Note.json');
});
it('should handle special characters in title', () => {
const conversation = createTestConversation('Test: Special & Characters!');
const path = service.generateConversationPath(conversation);
expect(path).toBe('Vault AI/Conversations/Test: Special & Characters!.json');
expect(path).toBe('Vaultkeeper AI/Conversations/Test: Special & Characters!.json');
});
it('should handle empty title', () => {
const conversation = createTestConversation('');
const path = service.generateConversationPath(conversation);
expect(path).toBe('Vault AI/Conversations/.json');
expect(path).toBe('Vaultkeeper AI/Conversations/.json');
});
});
@ -104,9 +104,9 @@ describe('ConversationFileSystemService - Integration Tests', () => {
const path = await service.saveConversation(conversation);
expect(path).toBe('Vault AI/Conversations/Test Save.json');
expect(path).toBe('Vaultkeeper AI/Conversations/Test Save.json');
expect(mockFileSystemService.writeObjectToFile).toHaveBeenCalledWith(
'Vault AI/Conversations/Test Save.json',
'Vaultkeeper AI/Conversations/Test Save.json',
expect.objectContaining({
title: 'Test Save',
created: '2024-01-01T10:00:00.000Z',
@ -160,7 +160,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
await service.saveConversation(conversation);
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/First Save.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/First Save.json');
});
it('should reuse current path on subsequent saves', async () => {
@ -177,7 +177,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
// Path should remain the same (uses cached path)
expect(firstPath).toBe(secondPath);
expect(secondPath).toBe('Vault AI/Conversations/Original Title.json');
expect(secondPath).toBe('Vaultkeeper AI/Conversations/Original Title.json');
});
it('should serialize function calls correctly', async () => {
@ -263,26 +263,26 @@ describe('ConversationFileSystemService - Integration Tests', () => {
const conversation = createTestConversation('Test Path');
await service.saveConversation(conversation);
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Test Path.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Test Path.json');
});
it('should return path after manual setting', () => {
service.setCurrentConversationPath('Vault AI/Conversations/Manual.json');
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Manual.json');
service.setCurrentConversationPath('Vaultkeeper AI/Conversations/Manual.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Manual.json');
});
});
describe('setCurrentConversationPath', () => {
it('should set the current path', () => {
service.setCurrentConversationPath('Vault AI/Conversations/Custom.json');
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Custom.json');
service.setCurrentConversationPath('Vaultkeeper AI/Conversations/Custom.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Custom.json');
});
it('should override previous path', () => {
service.setCurrentConversationPath('Vault AI/Conversations/First.json');
service.setCurrentConversationPath('Vault AI/Conversations/Second.json');
service.setCurrentConversationPath('Vaultkeeper AI/Conversations/First.json');
service.setCurrentConversationPath('Vaultkeeper AI/Conversations/Second.json');
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Second.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Second.json');
});
});
@ -307,7 +307,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
const conv2 = createTestConversation('Second');
await service.saveConversation(conv2);
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Second.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Second.json');
});
});
@ -322,7 +322,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
expect(result).toBe(true);
expect(mockFileSystemService.deleteFile).toHaveBeenCalledWith(
'Vault AI/Conversations/To Delete.json',
'Vaultkeeper AI/Conversations/To Delete.json',
true
);
expect(service.getCurrentConversationPath()).toBeNull();
@ -354,8 +354,8 @@ describe('ConversationFileSystemService - Integration Tests', () => {
describe('getAllConversations', () => {
it('should load all conversations from directory', async () => {
const mockFiles = [
createMockFile('Vault AI/Conversations/conv1.json'),
createMockFile('Vault AI/Conversations/conv2.json')
createMockFile('Vaultkeeper AI/Conversations/conv1.json'),
createMockFile('Vaultkeeper AI/Conversations/conv2.json')
];
mockFileSystemService.listFilesInDirectory.mockResolvedValue(mockFiles);
@ -400,14 +400,14 @@ describe('ConversationFileSystemService - Integration Tests', () => {
expect(conversations[0].title).toBe('Conversation 1');
expect(conversations[1].title).toBe('Conversation 2');
expect(mockFileSystemService.listFilesInDirectory).toHaveBeenCalledWith(
'Vault AI/Conversations',
'Vaultkeeper AI/Conversations',
false,
true
);
});
it('should reconstruct conversation objects correctly', async () => {
const mockFiles = [createMockFile('Vault AI/Conversations/test.json')];
const mockFiles = [createMockFile('Vaultkeeper AI/Conversations/test.json')];
mockFileSystemService.listFilesInDirectory.mockResolvedValue(mockFiles);
mockFileSystemService.readObjectFromFile.mockResolvedValue({
@ -449,8 +449,8 @@ describe('ConversationFileSystemService - Integration Tests', () => {
it('should skip invalid conversation files', async () => {
const mockFiles = [
createMockFile('Vault AI/Conversations/valid.json'),
createMockFile('Vault AI/Conversations/invalid.json')
createMockFile('Vaultkeeper AI/Conversations/valid.json'),
createMockFile('Vaultkeeper AI/Conversations/invalid.json')
];
mockFileSystemService.listFilesInDirectory.mockResolvedValue(mockFiles);
@ -482,7 +482,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
});
it('should reconstruct function call metadata', async () => {
const mockFiles = [createMockFile('Vault AI/Conversations/with-functions.json')];
const mockFiles = [createMockFile('Vaultkeeper AI/Conversations/with-functions.json')];
mockFileSystemService.listFilesInDirectory.mockResolvedValue(mockFiles);
mockFileSystemService.readObjectFromFile.mockResolvedValue({
@ -529,13 +529,13 @@ describe('ConversationFileSystemService - Integration Tests', () => {
mockFileSystemService.moveFile.mockResolvedValue({ success: true });
await service.updateConversationTitle(
'Vault AI/Conversations/Old Title.json',
'Vaultkeeper AI/Conversations/Old Title.json',
'New Title'
);
expect(mockFileSystemService.moveFile).toHaveBeenCalledWith(
'Vault AI/Conversations/Old Title.json',
'Vault AI/Conversations/New Title.json',
'Vaultkeeper AI/Conversations/Old Title.json',
'Vaultkeeper AI/Conversations/New Title.json',
true
);
});
@ -543,21 +543,21 @@ describe('ConversationFileSystemService - Integration Tests', () => {
it('should update current path if it matches old path', async () => {
mockFileSystemService.moveFile.mockResolvedValue({ success: true });
service.setCurrentConversationPath('Vault AI/Conversations/Old.json');
service.setCurrentConversationPath('Vaultkeeper AI/Conversations/Old.json');
await service.updateConversationTitle('Vault AI/Conversations/Old.json', 'New');
await service.updateConversationTitle('Vaultkeeper AI/Conversations/Old.json', 'New');
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/New.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/New.json');
});
it('should not update current path if it doesnt match', async () => {
mockFileSystemService.moveFile.mockResolvedValue({ success: true });
service.setCurrentConversationPath('Vault AI/Conversations/Other.json');
service.setCurrentConversationPath('Vaultkeeper AI/Conversations/Other.json');
await service.updateConversationTitle('Vault AI/Conversations/Old.json', 'New');
await service.updateConversationTitle('Vaultkeeper AI/Conversations/Old.json', 'New');
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Other.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Other.json');
});
it('should throw error when move fails', async () => {
@ -567,7 +567,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
});
await expect(
service.updateConversationTitle('Vault AI/Conversations/Old.json', 'New')
service.updateConversationTitle('Vaultkeeper AI/Conversations/Old.json', 'New')
).rejects.toThrow('Failed to update conversation title: Destination already exists');
});
@ -575,13 +575,13 @@ describe('ConversationFileSystemService - Integration Tests', () => {
mockFileSystemService.moveFile.mockResolvedValue({ success: true });
await service.updateConversationTitle(
'Vault AI/Conversations/Old.json',
'Vaultkeeper AI/Conversations/Old.json',
'New: Title & More!'
);
expect(mockFileSystemService.moveFile).toHaveBeenCalledWith(
'Vault AI/Conversations/Old.json',
'Vault AI/Conversations/New: Title & More!.json',
'Vaultkeeper AI/Conversations/Old.json',
'Vaultkeeper AI/Conversations/New: Title & More!.json',
true
);
});
@ -594,7 +594,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
await service.saveConversation(conversation);
const savedPath = service.getCurrentConversationPath();
expect(savedPath).toBe('Vault AI/Conversations/Original.json');
expect(savedPath).toBe('Vaultkeeper AI/Conversations/Original.json');
// Simulate loading conversations
const mockFiles = [createMockFile(savedPath!)];
@ -610,14 +610,14 @@ describe('ConversationFileSystemService - Integration Tests', () => {
mockFileSystemService.moveFile.mockResolvedValue({ success: true });
await service.updateConversationTitle(savedPath!, 'Updated Title');
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Updated Title.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Updated Title.json');
});
it('should handle save -> delete -> new save workflow', async () => {
// First conversation
const conv1 = createTestConversation('First');
await service.saveConversation(conv1);
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/First.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/First.json');
// Delete it
mockFileSystemService.deleteFile.mockResolvedValue({ success: true });
@ -627,7 +627,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
// New conversation
const conv2 = createTestConversation('Second');
await service.saveConversation(conv2);
expect(service.getCurrentConversationPath()).toBe('Vault AI/Conversations/Second.json');
expect(service.getCurrentConversationPath()).toBe('Vaultkeeper AI/Conversations/Second.json');
});
it('should preserve all conversation data through save/load cycle', async () => {
@ -662,7 +662,7 @@ describe('ConversationFileSystemService - Integration Tests', () => {
// Simulate load
mockFileSystemService.listFilesInDirectory.mockResolvedValue([
createMockFile('Vault AI/Conversations/Complete Test.json')
createMockFile('Vaultkeeper AI/Conversations/Complete Test.json')
]);
mockFileSystemService.readObjectFromFile.mockResolvedValue(savedData);

View file

@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { SettingsService, IVaultAISettings } from '../../Services/SettingsService';
import { SettingsService, IVaultkeeperAISettings } from '../../Services/SettingsService';
import { RegisterSingleton, DeregisterAllServices } from '../../Services/DependencyService';
import { Services } from '../../Services/Services';
import { AIProvider, AIProviderModel } from '../../Enums/ApiProvider';
@ -9,11 +9,11 @@ describe('SettingsService', () => {
let mockPlugin: any;
beforeEach(() => {
// Mock VaultAIPlugin
// Mock VaultkeeperAIPlugin
mockPlugin = {
saveData: vi.fn().mockResolvedValue(undefined)
};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
});
afterEach(() => {
@ -39,7 +39,7 @@ describe('SettingsService', () => {
});
it('should merge loaded settings with defaults', () => {
const loadedSettings: Partial<IVaultAISettings> = {
const loadedSettings: Partial<IVaultkeeperAISettings> = {
firstTimeStart: false,
model: AIProviderModel.GeminiFlash_2_5,
apiKeys: {
@ -51,7 +51,7 @@ describe('SettingsService', () => {
snippetSizeLimit: 200
};
settingsService = new SettingsService(loadedSettings as IVaultAISettings);
settingsService = new SettingsService(loadedSettings as IVaultkeeperAISettings);
expect(settingsService.settings.firstTimeStart).toBe(false);
expect(settingsService.settings.model).toBe(AIProviderModel.GeminiFlash_2_5);
@ -63,7 +63,7 @@ describe('SettingsService', () => {
});
it('should handle partially loaded settings and fill missing properties with defaults', () => {
const loadedSettings: Partial<IVaultAISettings> = {
const loadedSettings: Partial<IVaultkeeperAISettings> = {
model: AIProviderModel.GPT_4o,
apiKeys: {
claude: '',
@ -72,7 +72,7 @@ describe('SettingsService', () => {
}
};
settingsService = new SettingsService(loadedSettings as IVaultAISettings);
settingsService = new SettingsService(loadedSettings as IVaultkeeperAISettings);
expect(settingsService.settings.firstTimeStart).toBe(true); // Default
expect(settingsService.settings.model).toBe(AIProviderModel.GPT_4o); // Loaded
@ -86,7 +86,7 @@ describe('SettingsService', () => {
describe('getApiKeyForProvider', () => {
beforeEach(() => {
const loadedSettings: IVaultAISettings = {
const loadedSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.ClaudeSonnet_4_5,
apiKeys: {
@ -126,7 +126,7 @@ describe('SettingsService', () => {
describe('getApiKeyForCurrentModel', () => {
it('should return Claude key when current model is Claude', () => {
const loadedSettings: IVaultAISettings = {
const loadedSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.ClaudeSonnet_4_5,
apiKeys: {
@ -146,7 +146,7 @@ describe('SettingsService', () => {
});
it('should return OpenAI key when current model is GPT', () => {
const loadedSettings: IVaultAISettings = {
const loadedSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.GPT_4o,
apiKeys: {
@ -166,7 +166,7 @@ describe('SettingsService', () => {
});
it('should return Gemini key when current model is Gemini', () => {
const loadedSettings: IVaultAISettings = {
const loadedSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.GeminiFlash_2_5,
apiKeys: {
@ -211,7 +211,7 @@ describe('SettingsService', () => {
describe('setApiKeyForProvider', () => {
beforeEach(() => {
const loadedSettings: IVaultAISettings = {
const loadedSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.ClaudeSonnet_4_5,
apiKeys: {
@ -265,7 +265,7 @@ describe('SettingsService', () => {
describe('saveSettings', () => {
beforeEach(() => {
const loadedSettings: IVaultAISettings = {
const loadedSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.ClaudeSonnet_4_5,
apiKeys: {

View file

@ -43,7 +43,7 @@ describe('StatusBarService', () => {
mockPlugin = {
addStatusBarItem: vi.fn().mockReturnValue(mockStatusBarItem)
};
RegisterSingleton(Services.VaultAIPlugin, mockPlugin);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin);
service = new StatusBarService();
});

View file

@ -135,7 +135,7 @@ describe('VaultCacheService - Integration Tests', () => {
};
// Register dependencies
RegisterSingleton(Services.VaultAIPlugin, mockPlugin as any);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin as any);
RegisterSingleton(Services.VaultService, mockVaultService as any);
// Create fresh instance
@ -191,7 +191,7 @@ describe('VaultCacheService - Integration Tests', () => {
listDirectoryContents: mockListVaultContents
};
RegisterSingleton(Services.VaultAIPlugin, mockPluginWithMetadata as any);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPluginWithMetadata as any);
RegisterSingleton(Services.VaultService, mockVaultServiceWithContent as any);
// Create new instance to trigger initialization
@ -607,7 +607,7 @@ describe('VaultCacheService - Integration Tests', () => {
});
describe('folder event handling - exclusions', () => {
it('should not cache excluded folders on create (Vault AI directory)', () => {
it('should not cache excluded folders on create (Vaultkeeper AI directory)', () => {
// Mock VaultService to return null for excluded paths
const mockVaultServiceWithExclusions = {
registerFileEvents: vi.fn((handler: any) => {
@ -616,22 +616,22 @@ describe('VaultCacheService - Integration Tests', () => {
listDirectoryContents: vi.fn(async () => []),
getAbstractFileByPath: vi.fn((path: string) => {
// Return null for excluded paths (simulating VaultService exclusion logic)
if (path.startsWith('Vault AI')) {
if (path.startsWith('Vaultkeeper AI')) {
return null;
}
// Return a folder for allowed paths
return createMockFolder(path);
}),
isExclusion: vi.fn((path: string) => {
// Return true for Vault AI directory paths
return path.startsWith('Vault AI');
// Return true for Vaultkeeper AI directory paths
return path.startsWith('Vaultkeeper AI');
})
};
RegisterSingleton(Services.VaultService, mockVaultServiceWithExclusions as any);
vaultCacheService = new VaultCacheService();
const excludedFolder = createMockFolder('Vault AI/subfolder');
const excludedFolder = createMockFolder('Vaultkeeper AI/subfolder');
fileEventHandler(FileEvent.Create, excludedFolder, { oldPath: '' });
const results = vaultCacheService.matchFolder('subfolder');
@ -670,7 +670,7 @@ describe('VaultCacheService - Integration Tests', () => {
})
};
RegisterSingleton(Services.VaultAIPlugin, mockPluginWithExclusions as any);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPluginWithExclusions as any);
RegisterSingleton(Services.VaultService, mockVaultServiceWithExclusions as any);
vaultCacheService = new VaultCacheService();
@ -693,15 +693,15 @@ describe('VaultCacheService - Integration Tests', () => {
if (path === 'public-folder') {
return createMockFolder(path);
}
// Second call (Vault AI/renamed) should fail
if (path.startsWith('Vault AI')) {
// Second call (Vaultkeeper AI/renamed) should fail
if (path.startsWith('Vaultkeeper AI')) {
return null;
}
return createMockFolder(path);
}),
isExclusion: vi.fn((path: string) => {
// Return true for Vault AI paths
return path.startsWith('Vault AI');
// Return true for Vaultkeeper AI paths
return path.startsWith('Vaultkeeper AI');
})
};
@ -717,7 +717,7 @@ describe('VaultCacheService - Integration Tests', () => {
expect(results.length).toBeGreaterThan(0);
// Rename to excluded path
const renamedFolder = createMockFolder('Vault AI/renamed');
const renamedFolder = createMockFolder('Vaultkeeper AI/renamed');
fileEventHandler(FileEvent.Rename, renamedFolder, { oldPath: folder.path });
// Should not be in cache under new name
@ -737,14 +737,14 @@ describe('VaultCacheService - Integration Tests', () => {
}),
listDirectoryContents: vi.fn(async () => []),
getAbstractFileByPath: vi.fn((path: string) => {
if (path.startsWith('Vault AI')) {
if (path.startsWith('Vaultkeeper AI')) {
return null;
}
return createMockFolder(path);
}),
isExclusion: vi.fn((path: string) => {
// Return true for Vault AI paths, false for others
return path.startsWith('Vault AI');
// Return true for Vaultkeeper AI paths, false for others
return path.startsWith('Vaultkeeper AI');
})
};

View file

@ -5,7 +5,7 @@ import { Path } from '../../Enums/Path';
import { RegisterSingleton, DeregisterAllServices } from '../../Services/DependencyService';
import { Services } from '../../Services/Services';
import { SanitiserService } from '../../Services/SanitiserService';
import { SettingsService, IVaultAISettings } from '../../Services/SettingsService';
import { SettingsService, IVaultkeeperAISettings } from '../../Services/SettingsService';
import { AIProviderModel } from '../../Enums/ApiProvider';
/**
@ -43,7 +43,7 @@ const mockFileManager = {
};
// Create a mutable settings object that tests can modify
const mockSettings: IVaultAISettings = {
const mockSettings: IVaultkeeperAISettings = {
firstTimeStart: false,
model: AIProviderModel.ClaudeSonnet_4_5,
apiKeys: {
@ -114,7 +114,7 @@ describe('VaultService - Integration Tests', () => {
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
// Register real dependencies in DependencyService
RegisterSingleton(Services.VaultAIPlugin, mockPlugin as any);
RegisterSingleton(Services.VaultkeeperAIPlugin, mockPlugin as any);
RegisterSingleton(Services.FileManager, mockFileManager);
RegisterSingleton(Services.SanitiserService, new SanitiserService());
@ -147,11 +147,11 @@ describe('VaultService - Integration Tests', () => {
expect(mockVault.getMarkdownFiles).toHaveBeenCalledOnce();
});
it('should filter out files in the Vault AI root directory by default', () => {
it('should filter out files in the Vaultkeeper AI root directory by default', () => {
const files = [
createMockFile('note1.md'),
createMockFile('Vault AI/conversation.md'),
createMockFile('Vault AI/subfolder/data.md')
createMockFile('Vaultkeeper AI/conversation.md'),
createMockFile('Vaultkeeper AI/subfolder/data.md')
];
mockVault.getMarkdownFiles.mockReturnValue(files);
@ -161,10 +161,10 @@ describe('VaultService - Integration Tests', () => {
expect(result[0].path).toBe('note1.md');
});
it('should allow access to Vault AI directory when allowAccessToPluginRoot is true', () => {
it('should allow access to Vaultkeeper AI directory when allowAccessToPluginRoot is true', () => {
const files = [
createMockFile('note1.md'),
createMockFile('Vault AI/conversation.md')
createMockFile('Vaultkeeper AI/conversation.md')
];
mockVault.getMarkdownFiles.mockReturnValue(files);
@ -202,9 +202,9 @@ describe('VaultService - Integration Tests', () => {
});
it('should return null and log error when path is excluded', () => {
mockVault.getAbstractFileByPath.mockReturnValue(createMockFile('Vault AI/test.md'));
mockVault.getAbstractFileByPath.mockReturnValue(createMockFile('Vaultkeeper AI/test.md'));
const result = vaultService.getAbstractFileByPath('Vault AI/test.md', false);
const result = vaultService.getAbstractFileByPath('Vaultkeeper AI/test.md', false);
expect(result).toBeNull();
expect(consoleErrorSpy).toHaveBeenCalled();
@ -223,29 +223,29 @@ describe('VaultService - Integration Tests', () => {
});
it('should allow access to excluded paths when allowAccessToPluginRoot is true', () => {
const mockFile = createMockFile('Vault AI/conversation.md');
const mockFile = createMockFile('Vaultkeeper AI/conversation.md');
mockVault.getAbstractFileByPath.mockReturnValue(mockFile);
const result = vaultService.getAbstractFileByPath('Vault AI/conversation.md', true);
const result = vaultService.getAbstractFileByPath('Vaultkeeper AI/conversation.md', true);
expect(result).toBe(mockFile);
});
it('should exclude the Vault AI directory itself', () => {
const mockFolder = createMockFolder('Vault AI');
it('should exclude the Vaultkeeper AI directory itself', () => {
const mockFolder = createMockFolder('Vaultkeeper AI');
mockVault.getAbstractFileByPath.mockReturnValue(mockFolder);
const result = vaultService.getAbstractFileByPath('Vault AI', false);
const result = vaultService.getAbstractFileByPath('Vaultkeeper AI', false);
expect(result).toBeNull();
expect(consoleErrorSpy).toHaveBeenCalled();
});
it('should allow access to Vault AI directory when allowAccessToPluginRoot is true', () => {
const mockFolder = createMockFolder('Vault AI');
it('should allow access to Vaultkeeper AI directory when allowAccessToPluginRoot is true', () => {
const mockFolder = createMockFolder('Vaultkeeper AI');
mockVault.getAbstractFileByPath.mockReturnValue(mockFolder);
const result = vaultService.getAbstractFileByPath('Vault AI', true);
const result = vaultService.getAbstractFileByPath('Vaultkeeper AI', true);
expect(result).toBe(mockFolder);
});
@ -261,7 +261,7 @@ describe('VaultService - Integration Tests', () => {
});
it('should return false when file is excluded', async () => {
const result = await vaultService.exists('Vault AI/test.md', false);
const result = await vaultService.exists('Vaultkeeper AI/test.md', false);
expect(result).toBe(false);
expect(consoleErrorSpy).toHaveBeenCalled();
@ -296,7 +296,7 @@ describe('VaultService - Integration Tests', () => {
});
it('should return empty string and log error when file is excluded', async () => {
const mockFile = createMockFile('Vault AI/test.md');
const mockFile = createMockFile('Vaultkeeper AI/test.md');
const result = await vaultService.read(mockFile, false);
@ -306,7 +306,7 @@ describe('VaultService - Integration Tests', () => {
});
it('should allow reading excluded files when allowAccessToPluginRoot is true', async () => {
const mockFile = createMockFile('Vault AI/test.md');
const mockFile = createMockFile('Vaultkeeper AI/test.md');
mockVault.read.mockResolvedValue('content');
const result = await vaultService.read(mockFile, true);
@ -331,7 +331,7 @@ describe('VaultService - Integration Tests', () => {
it('should throw error when trying to create file in excluded path', async () => {
await expect(
vaultService.create('Vault AI/test.md', 'content', false)
vaultService.create('Vaultkeeper AI/test.md', 'content', false)
).rejects.toThrow('Plugin attempted to create a file that is in the exclusion list');
});
@ -374,7 +374,7 @@ describe('VaultService - Integration Tests', () => {
});
it('should not modify file and log error when file is excluded', async () => {
const mockFile = createMockFile('Vault AI/test.md');
const mockFile = createMockFile('Vaultkeeper AI/test.md');
await vaultService.modify(mockFile, 'new content', false);
@ -409,7 +409,7 @@ describe('VaultService - Integration Tests', () => {
});
it('should not delete file and return error when excluded', async () => {
const mockFile = createMockFile('Vault AI/test.md');
const mockFile = createMockFile('Vaultkeeper AI/test.md');
const result = await vaultService.delete(mockFile, false);
@ -460,7 +460,7 @@ describe('VaultService - Integration Tests', () => {
});
it('should return error when source file is excluded', async () => {
const result = await vaultService.move('Vault AI/test.md', 'dest.md', false);
const result = await vaultService.move('Vaultkeeper AI/test.md', 'dest.md', false);
expect(result).toEqual({ success: false, error: 'Source file is in exclusion list' });
expect(mockFileManager.renameFile).not.toHaveBeenCalled();
@ -515,7 +515,7 @@ describe('VaultService - Integration Tests', () => {
it('should throw error when trying to create folder in excluded path', async () => {
await expect(
vaultService.createFolder('Vault AI/subfolder', false)
vaultService.createFolder('Vaultkeeper AI/subfolder', false)
).rejects.toThrow('Plugin attempted to create a folder that is in the exclusion list');
});
});
@ -586,9 +586,9 @@ describe('VaultService - Integration Tests', () => {
});
it('should respect allowAccessToPluginRoot parameter when accessing directory', async () => {
const file1 = createMockFile('Vault AI/test.md');
const file2 = createMockFile('Vault AI/notes.md');
const agentFolder = createMockFolder('Vault AI', [file1, file2]);
const file1 = createMockFile('Vaultkeeper AI/test.md');
const file2 = createMockFile('Vaultkeeper AI/notes.md');
const agentFolder = createMockFolder('Vaultkeeper AI', [file1, file2]);
mockVault.getAbstractFileByPath.mockReturnValue(agentFolder);
@ -596,19 +596,19 @@ describe('VaultService - Integration Tests', () => {
const getAbstractFileSpy = vi.spyOn(vaultService, 'getAbstractFileByPath');
// Call with allowAccessToPluginRoot = false (should block access)
await vaultService.listFilesInDirectory('Vault AI', true, false);
await vaultService.listFilesInDirectory('Vaultkeeper AI', true, false);
// Verify getAbstractFileByPath was called with the correct parameter
expect(getAbstractFileSpy).toHaveBeenCalledWith('Vault AI', false);
expect(getAbstractFileSpy).toHaveBeenCalledWith('Vaultkeeper AI', false);
// Reset
getAbstractFileSpy.mockClear();
// Call with allowAccessToPluginRoot = true (should allow access)
await vaultService.listFilesInDirectory('Vault AI', true, true);
await vaultService.listFilesInDirectory('Vaultkeeper AI', true, true);
// Verify getAbstractFileByPath was called with the correct parameter
expect(getAbstractFileSpy).toHaveBeenCalledWith('Vault AI', true);
expect(getAbstractFileSpy).toHaveBeenCalledWith('Vaultkeeper AI', true);
});
it('should not access excluded directory when allowAccessToPluginRoot is false', async () => {
@ -618,11 +618,11 @@ describe('VaultService - Integration Tests', () => {
// Create a spy to verify the correct parameter is passed
const getAbstractFileSpy = vi.spyOn(vaultService, 'getAbstractFileByPath');
// Try to list files in Vault AI directory with allowAccessToPluginRoot = false
const result = await vaultService.listFilesInDirectory('Vault AI', true, false);
// Try to list files in Vaultkeeper AI directory with allowAccessToPluginRoot = false
const result = await vaultService.listFilesInDirectory('Vaultkeeper AI', true, false);
// Should call getAbstractFileByPath with false (not hardcoded true)
expect(getAbstractFileSpy).toHaveBeenCalledWith('Vault AI', false);
expect(getAbstractFileSpy).toHaveBeenCalledWith('Vaultkeeper AI', false);
// Should return empty array since directory is excluded
expect(result).toEqual([]);
@ -697,33 +697,33 @@ describe('VaultService - Integration Tests', () => {
});
it('should respect allowAccessToPluginRoot parameter', async () => {
const agentFolder = createMockFolder('Vault AI', []);
const agentFolder = createMockFolder('Vaultkeeper AI', []);
const notesFolder = createMockFolder('notes', []);
const rootFolder = createMockFolder('/', [agentFolder, notesFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
// Vault AI blocked when allowAccessToPluginRoot = false
if (path === 'Vault AI') return null;
// Vaultkeeper AI blocked when allowAccessToPluginRoot = false
if (path === 'Vaultkeeper AI') return null;
if (path === 'notes') return notesFolder;
return null;
});
// With allowAccessToPluginRoot = false (should exclude Vault AI)
// With allowAccessToPluginRoot = false (should exclude Vaultkeeper AI)
const result1 = await vaultService.listFoldersInDirectory('/', true, false);
expect(result1.some((folder) => folder.path === 'Vault AI')).toBe(false);
expect(result1.some((folder) => folder.path === 'Vaultkeeper AI')).toBe(false);
expect(result1.some((folder) => folder.path === 'notes')).toBe(true);
// With allowAccessToPluginRoot = true (should include Vault AI)
// With allowAccessToPluginRoot = true (should include Vaultkeeper AI)
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
if (path === 'Vault AI') return agentFolder; // Now allowed
if (path === 'Vaultkeeper AI') return agentFolder; // Now allowed
if (path === 'notes') return notesFolder;
return null;
});
const result2 = await vaultService.listFoldersInDirectory('/', true, true);
expect(result2.some((folder) => folder.path === 'Vault AI')).toBe(true);
expect(result2.some((folder) => folder.path === 'Vaultkeeper AI')).toBe(true);
expect(result2.some((folder) => folder.path === 'notes')).toBe(true);
});
@ -905,19 +905,19 @@ describe('VaultService - Integration Tests', () => {
});
it('should not log errors when vault contains excluded directories', async () => {
// Setup: Create a vault structure with excluded "Vault AI" directory
// Setup: Create a vault structure with excluded "Vaultkeeper AI" directory
const normalFile = createMockFile('notes/document.md');
const excludedFile = createMockFile('Vault AI/secret.md');
const excludedFile = createMockFile('Vaultkeeper AI/secret.md');
const notesFolder = createMockFolder('notes', [normalFile]);
const excludedFolder = createMockFolder('Vault AI', [excludedFile]);
const excludedFolder = createMockFolder('Vaultkeeper AI', [excludedFile]);
const rootFolder = createMockFolder('/', [normalFile, notesFolder, excludedFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
if (path === 'notes') return notesFolder;
if (path === 'notes/document.md') return normalFile;
// Vault AI directory should be blocked by getAbstractFileByPath
if (path === 'Vault AI') return null;
// Vaultkeeper AI directory should be blocked by getAbstractFileByPath
if (path === 'Vaultkeeper AI') return null;
return null;
});
@ -941,16 +941,16 @@ describe('VaultService - Integration Tests', () => {
});
it('should respect allowAccessToPluginRoot parameter when true', async () => {
// Setup: Create vault with Vault AI directory
const agentFile = createMockFile('Vault AI/notes.md');
// Setup: Create vault with Vaultkeeper AI directory
const agentFile = createMockFile('Vaultkeeper AI/notes.md');
const normalFile = createMockFile('normal.md');
const agentFolder = createMockFolder('Vault AI', [agentFile]);
const agentFolder = createMockFolder('Vaultkeeper AI', [agentFile]);
const rootFolder = createMockFolder('/', [normalFile, agentFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
if (path === 'Vault AI') return agentFolder;
if (path === 'Vault AI/notes.md') return agentFile;
if (path === 'Vaultkeeper AI') return agentFolder;
if (path === 'Vaultkeeper AI/notes.md') return agentFile;
if (path === 'normal.md') return normalFile;
return null;
});
@ -960,23 +960,23 @@ describe('VaultService - Integration Tests', () => {
// Search with allowAccessToPluginRoot = true
const results = await vaultService.searchVaultFiles('searchable', true);
// Should include files from Vault AI directory
// Should include files from Vaultkeeper AI directory
const paths = results.map(r => r.file.path);
expect(paths).toContain('Vault AI/notes.md');
expect(paths).toContain('Vaultkeeper AI/notes.md');
expect(paths).toContain('normal.md');
});
it('should exclude Vault AI directory when allowAccessToPluginRoot is false', async () => {
// Setup: Create vault with Vault AI directory
const agentFile = createMockFile('Vault AI/notes.md');
it('should exclude Vaultkeeper AI directory when allowAccessToPluginRoot is false', async () => {
// Setup: Create vault with Vaultkeeper AI directory
const agentFile = createMockFile('Vaultkeeper AI/notes.md');
const normalFile = createMockFile('normal.md');
const agentFolder = createMockFolder('Vault AI', [agentFile]);
const agentFolder = createMockFolder('Vaultkeeper AI', [agentFile]);
const rootFolder = createMockFolder('/', [normalFile, agentFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
// When allowAccessToPluginRoot is false, Vault AI should be blocked
if (path === 'Vault AI') return null;
// When allowAccessToPluginRoot is false, Vaultkeeper AI should be blocked
if (path === 'Vaultkeeper AI') return null;
if (path === 'normal.md') return normalFile;
return null;
});
@ -986,9 +986,9 @@ describe('VaultService - Integration Tests', () => {
// Search with allowAccessToPluginRoot = false (default)
const results = await vaultService.searchVaultFiles('searchable', false);
// Should NOT include files from Vault AI directory
// Should NOT include files from Vaultkeeper AI directory
const paths = results.map(r => r.file.path);
expect(paths).not.toContain('Vault AI/notes.md');
expect(paths).not.toContain('Vaultkeeper AI/notes.md');
expect(paths).toContain('normal.md');
});
@ -1057,8 +1057,8 @@ describe('VaultService - Integration Tests', () => {
expect(await vaultService.exists('temp/sub/file.md')).toBe(false);
});
it('should always exclude Vault AI root by default', async () => {
const result = await vaultService.exists('Vault AI/file.md', false);
it('should always exclude Vaultkeeper AI root by default', async () => {
const result = await vaultService.exists('Vaultkeeper AI/file.md', false);
expect(result).toBe(false);
});
@ -1166,17 +1166,17 @@ describe('VaultService - Integration Tests', () => {
it('should filter out excluded files and folders', async () => {
const publicNote = createMockFile('public/note.md');
const agentNote = createMockFile('Vault AI/conversation.md');
const agentNote = createMockFile('Vaultkeeper AI/conversation.md');
const privateNote = createMockFile('private/secret.md');
const publicFolder = createMockFolder('public', [publicNote]);
const agentFolder = createMockFolder('Vault AI', [agentNote]);
const agentFolder = createMockFolder('Vaultkeeper AI', [agentNote]);
const privateFolder = createMockFolder('private', [privateNote]);
const rootFolder = createMockFolder('/', [publicNote, agentNote, privateNote, publicFolder, agentFolder, privateFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
if (path === 'public') return publicFolder;
if (path === 'Vault AI') return null; // Excluded by default
if (path === 'Vaultkeeper AI') return null; // Excluded by default
if (path === 'private') return privateFolder;
return null;
});
@ -1186,54 +1186,54 @@ describe('VaultService - Integration Tests', () => {
const result = await vaultService.listDirectoryContents(Path.Root, true, false);
// Should include: public/note.md and public folder
// Should exclude: Vault AI folder (default exclusion), private/** content
// Should exclude: Vaultkeeper AI folder (default exclusion), private/** content
expect(result.some((item: any) => item.path === 'public/note.md')).toBe(true);
expect(result.some((item: any) => item.path === 'public')).toBe(true);
expect(result.some((item: any) => item.path === 'Vault AI/conversation.md')).toBe(false);
expect(result.some((item: any) => item.path === 'Vaultkeeper AI/conversation.md')).toBe(false);
expect(result.some((item: any) => item.path === 'private/secret.md')).toBe(false);
expect(result.some((item: any) => item.path === 'private')).toBe(true); // Folder itself not excluded by 'private/**'
});
it('should exclude the Vault AI directory itself from folder listings', async () => {
it('should exclude the Vaultkeeper AI directory itself from folder listings', async () => {
const publicNote = createMockFile('public/note.md');
const publicFolder = createMockFolder('public', [publicNote]);
const agentFolder = createMockFolder('Vault AI', []);
const agentFolder = createMockFolder('Vaultkeeper AI', []);
const notesFolder = createMockFolder('notes', []);
const rootFolder = createMockFolder('/', [publicNote, publicFolder, agentFolder, notesFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
if (path === 'public') return publicFolder;
if (path === 'Vault AI') return null; // Excluded by default
if (path === 'Vaultkeeper AI') return null; // Excluded by default
if (path === 'notes') return notesFolder;
return null;
});
const result = await vaultService.listDirectoryContents(Path.Root, true, false);
// Vault AI directory itself should be excluded
expect(result.some((item: any) => item.path === 'Vault AI')).toBe(false);
// Vaultkeeper AI directory itself should be excluded
expect(result.some((item: any) => item.path === 'Vaultkeeper AI')).toBe(false);
// Other folders should be included
expect(result.some((item: any) => item.path === 'public')).toBe(true);
expect(result.some((item: any) => item.path === 'notes')).toBe(true);
});
it('should include Vault AI directory when allowAccessToPluginRoot is true', async () => {
it('should include Vaultkeeper AI directory when allowAccessToPluginRoot is true', async () => {
const note = createMockFile('note.md');
const agentNote = createMockFile('Vault AI/conversation.md');
const agentFolder = createMockFolder('Vault AI', [agentNote]);
const agentNote = createMockFile('Vaultkeeper AI/conversation.md');
const agentFolder = createMockFolder('Vaultkeeper AI', [agentNote]);
const rootFolder = createMockFolder('/', [note, agentFolder]);
mockVault.getAbstractFileByPath.mockImplementation((path: string) => {
if (path === '/' || path === '') return rootFolder;
if (path === 'Vault AI') return agentFolder; // Allowed with flag
if (path === 'Vaultkeeper AI') return agentFolder; // Allowed with flag
return null;
});
const result = await vaultService.listDirectoryContents(Path.Root, true, true);
expect(result).toHaveLength(3);
expect(result.some((item: any) => item.path === 'Vault AI/conversation.md')).toBe(true);
expect(result.some((item: any) => item.path === 'Vaultkeeper AI/conversation.md')).toBe(true);
});
it('should return empty array when vault is empty', async () => {

12
main.ts
View file

@ -1,7 +1,7 @@
import { WorkspaceLeaf, Plugin } from "obsidian";
import { MainView, VIEW_TYPE_MAIN } from "Views/MainView";
import { RegisterDependencies, RegisterPlugin } from "Services/ServiceRegistration";
import { VaultAISettingTab } from "VaultAISettingTab";
import { VaultkeeperAISettingTab } from "VaultkeeperAISettingTab";
import { Services } from "Services/Services";
import type { StatusBarService } from "Services/StatusBarService";
import { DeregisterAllServices, Resolve } from "Services/DependencyService";
@ -10,7 +10,7 @@ import { Path } from "Enums/Path";
import { Copy } from "Enums/Copy";
import type { SettingsService } from "Services/SettingsService";
export default class VaultAIPlugin extends Plugin {
export default class VaultkeeperAIPlugin extends Plugin {
public async onload() {
// KaTeX CSS is bundled with the plugin to comply with CSP
@ -27,18 +27,18 @@ export default class VaultAIPlugin extends Plugin {
);
this.addCommand({
id: "vault-ai",
name: "Vault AI",
id: "vaultkeeper-ai",
name: "Vaultkeeper AI",
callback: () => {
this.activateView();
}
});
this.addRibbonIcon("sparkles", "Vault AI", (_: MouseEvent) => {
this.addRibbonIcon("sparkles", "Vaultkeeper AI", (_: MouseEvent) => {
this.activateView();
});
this.addSettingTab(new VaultAISettingTab());
this.addSettingTab(new VaultkeeperAISettingTab());
this.app.workspace.onLayoutReady(async () => {
await this.setup();

View file

@ -1,6 +1,6 @@
{
"id": "vault-ai",
"name": "Vault AI",
"id": "vaultkeeper-ai",
"name": "Vaultkeeper AI",
"version": "1.0.1",
"minAppVersion": "1.9.14",
"description": "Multi-AI assistant for Obsidian. Read, search, create, and edit notes with multiple AI providers.",

4
package-lock.json generated
View file

@ -1,11 +1,11 @@
{
"name": "vault-ai",
"name": "vaultkeeper-ai",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vault-ai",
"name": "vaultkeeper-ai",
"version": "1.0.0",
"license": "MIT",
"dependencies": {

View file

@ -1,5 +1,5 @@
{
"name": "vault-ai",
"name": "vaultkeeper-ai",
"version": "1.0.0",
"description": "AI powered agent assistant.",
"main": "main.js",

View file

@ -2,11 +2,11 @@
/* Main View Customization */
/* ============================== */
.workspace-leaf-content[data-type="vault-ai-main-view"] {
.workspace-leaf-content[data-type="vaultkeeper-ai-main-view"] {
background-color: var(--background-secondary);
}
.workspace-leaf-content[data-type="vault-ai-main-view"] .view-content {
.workspace-leaf-content[data-type="vaultkeeper-ai-main-view"] .view-content {
align-self: center;
max-width: 1000px;
background-color: var(--background-secondary);