diff --git a/AGENTS.md b/AGENTS.md index d81feeaa..5acef64b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,7 @@ Copilot for Obsidian is an AI-powered assistant plugin that integrates various L - **NEVER RUN `npm run dev`** - The user will handle all builds manually - `npm run build` - Production build (TypeScript check + minified output) +- `npm run test:vault` - macOS only. Installs deps, builds, symlinks `main.js` / `manifest.json` / `styles.css` from the current worktree into `$COPILOT_TEST_VAULT_PATH/.obsidian/plugins/copilot/`, then reloads the plugin via the Obsidian CLI. Requires the user-level env var `COPILOT_TEST_VAULT_PATH` to be set to a vault that has been opened in Obsidian at least once. Use this when the user asks you to load the plugin into their test vault — it replaces manual build + copy + reload. ### Code Quality diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 937908e1..2cd86b83 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,6 +53,34 @@ In the case of Copilot for Obsidian, you will need to: Try to be descriptive in your branch names and pull requests. Happy coding! +#### Fast Iteration with `npm run test:vault` (macOS) + +If you work across multiple worktrees or just want one command to build and load the plugin into a test vault, use `npm run test:vault`. It runs `npm install`, builds, symlinks `main.js` / `manifest.json` / `styles.css` from the worktree into the vault's `.obsidian/plugins/copilot/` folder, and reloads the plugin in Obsidian via its CLI. + +**One-time setup:** + +1. Create or pick a vault dedicated to plugin testing and open it in Obsidian at least once so `.obsidian/` is created. +2. Enable community plugins in that vault (Settings → Community plugins → Turn on). +3. Set an env var pointing at the vault path. Add this to `~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish`: + + ```bash + export COPILOT_TEST_VAULT_PATH="$HOME/Obsidian/CopilotTestVault" + ``` + +**Per change:** + +From any worktree, run: + +```bash +npm run test:vault +``` + +The script installs deps, builds the plugin, symlinks the build artifacts into the vault, then calls `plugin:enable` and `plugin:reload` on the Obsidian CLI. If Obsidian isn't running, the symlinks are still in place — start Obsidian and the new build will load. + +Because the script symlinks files (not the worktree root), the vault's plugin `data.json` (settings, chat history) stays vault-local and is preserved across worktrees and rebuilds. + +Requires macOS with Obsidian installed at `/Applications/Obsidian.app`. + ## Commit Signing Commits to `master` must be signed and verified by GitHub. The easiest path is SSH signing using your existing SSH key. diff --git a/package.json b/package.json index f5f00067..2fb82408 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "test": "jest --testPathIgnorePatterns=src/integration_tests/", "test:integration": "jest src/integration_tests/", "prepare": "husky", - "prompt:debug": "node scripts/printPromptDebug.js" + "prompt:debug": "node scripts/printPromptDebug.js", + "test:vault": "bash scripts/test-vault.sh" }, "keywords": [], "author": "Logan Yang", diff --git a/scripts/test-vault.sh b/scripts/test-vault.sh new file mode 100755 index 00000000..17e2b6a3 --- /dev/null +++ b/scripts/test-vault.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +set -euo pipefail + +OBSIDIAN_BIN="/Applications/Obsidian.app/Contents/MacOS/obsidian" + +if [[ -z "${COPILOT_TEST_VAULT_PATH:-}" ]]; then + cat >&2 <<'EOF' +error: COPILOT_TEST_VAULT_PATH is not set. + +Set it once at the user level (e.g. in ~/.zshrc or ~/.config/fish/config.fish) +to the absolute path of an Obsidian vault you've opened at least once: + + export COPILOT_TEST_VAULT_PATH="$HOME/Obsidian/CopilotTestVault" + +Then re-run: npm run test:vault +EOF + exit 1 +fi + +VAULT_PATH="$COPILOT_TEST_VAULT_PATH" + +if [[ ! -d "$VAULT_PATH" ]]; then + echo "error: vault directory not found: $VAULT_PATH" >&2 + exit 1 +fi + +if [[ ! -d "$VAULT_PATH/.obsidian" ]]; then + echo "error: $VAULT_PATH has no .obsidian/ folder." >&2 + echo "Open the folder as a vault in Obsidian once, then re-run." >&2 + exit 1 +fi + +WORKTREE_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$WORKTREE_ROOT" + +echo "==> Installing dependencies" +npm install --prefer-offline --no-audit --no-fund + +echo "==> Building plugin" +npm run build + +PLUGIN_ID="$(node -p "require('./manifest.json').id")" +if [[ -z "$PLUGIN_ID" ]]; then + echo "error: could not read plugin id from manifest.json" >&2 + exit 1 +fi + +PLUGIN_DIR="$VAULT_PATH/.obsidian/plugins/$PLUGIN_ID" +mkdir -p "$PLUGIN_DIR" + +echo "==> Linking artifacts into $PLUGIN_DIR" +for f in main.js manifest.json styles.css; do + if [[ ! -f "$WORKTREE_ROOT/$f" ]]; then + echo "error: expected build artifact missing: $WORKTREE_ROOT/$f" >&2 + exit 1 + fi + ln -sfn "$WORKTREE_ROOT/$f" "$PLUGIN_DIR/$f" +done + +echo "==> Reloading plugin in Obsidian" +if [[ ! -x "$OBSIDIAN_BIN" ]]; then + echo "warning: Obsidian CLI not found at $OBSIDIAN_BIN; skipping reload." >&2 +else + if ! "$OBSIDIAN_BIN" plugin:enable id="$PLUGIN_ID" >/dev/null 2>&1 \ + || ! "$OBSIDIAN_BIN" plugin:reload id="$PLUGIN_ID" >/dev/null 2>&1; then + echo "warning: Obsidian doesn't appear to be running. Start it and the symlinked plugin will load on next open." >&2 + fi +fi + +echo +echo "Done." +echo " worktree: $WORKTREE_ROOT" +echo " vault: $VAULT_PATH" +echo " plugin: $PLUGIN_ID"