diff --git a/.junie/AGENTS.md b/.junie/AGENTS.md
new file mode 100644
index 0000000..d6e2f20
--- /dev/null
+++ b/.junie/AGENTS.md
@@ -0,0 +1,133 @@
+# Zhiju AI Assistant — Developer Notes
+
+## Project Overview
+
+Electron desktop app (Electron Forge + Vite + Vue 3) that wraps a locally-spawned `opencode` binary (located in `resources/`). The renderer is a Vue 3 SPA; the main process manages the `opencode` child process and exposes IPC to the renderer via a preload script.
+
+---
+
+## Build & Configuration
+
+### Prerequisites
+- Node.js ≥ 18
+- The `opencode` binary must exist at `resources/windows/x64/opencode.exe` (Windows) before packaging. It is **not** committed to the repo — obtain it separately.
+
+### Install dependencies
+```bash
+npm install
+```
+
+### Run in development
+```bash
+npm start
+```
+This uses `electron-forge start`, which runs Vite for the renderer and launches Electron. Hot-reload is active for the renderer; changes to `src/main/index.js` or `src/preload/index.js` require a manual restart.
+
+### Package / distribute
+```bash
+npm run make # builds installers for the current platform
+npm run package # produces an unpackaged app directory
+```
+Packaged output lands in `out/`. The `opencode.exe` binary is bundled via `extraResource` in `forge.config.js` and unpacked from ASAR at runtime.
+
+### Vite configs
+| File | Purpose |
+|---|---|
+| `vite.main.config.mjs` | Main process bundle |
+| `vite.preload.config.mjs` | Preload script bundle |
+| `vite.renderer.config.mjs` | Renderer (Vue SPA), alias `@` → `src/renderer` |
+
+---
+
+## Architecture Notes
+
+- **Main process** (`src/main/index.js`): spawns `opencode` binary, resolves a free port starting at `4096`, waits for TCP readiness, then injects `window.__opencodeBaseUrl` into the renderer via `executeJavaScript`. Also runs Bonjour service discovery.
+- **Preload** (`src/preload/index.js`): bridges IPC between main and renderer using `contextBridge`.
+- **Renderer** (`src/renderer/`): Vue 3 + Pinia + Vue Router. HTTP calls go through `src/renderer/http/` (axios-based). SSE streaming is handled in `src/renderer/http/sse.js`.
+- **URL constants** (`src/renderer/http/url.js`): single source of truth for all API endpoint paths. `getBaseUrl()` reads `window.__opencodeBaseUrl` (injected by main) with fallback to `http://127.0.0.1:4096`.
+- **Crypto** (`src/renderer/utils/crypto.js`): passwords are Base64-encoded then RSA-encrypted (public key hardcoded) before being sent to the login API.
+
+---
+
+## Testing
+
+### Framework
+[Vitest](https://vitest.dev/) — chosen for native Vite/ESM compatibility, zero extra config needed.
+
+### Run all tests
+```bash
+npm test # vitest run (single pass, CI-friendly)
+npm run test:watch # vitest watch mode (development)
+```
+
+Or directly:
+```bash
+npx vitest run
+```
+
+### Writing tests
+- Place test files alongside the source file as `*.test.js` (Vitest picks them up automatically).
+- For pure utility/logic modules (e.g. `url.js`, `crypto.js`) no additional setup is needed.
+- Modules that depend on `window`, `electron`, or Pinia require mocking. Use `vi.stubGlobal` for `window` properties and `vi.mock` for module mocks.
+
+### Example: testing `url.js`
+```js
+// src/renderer/http/url.test.js
+import { describe, it, expect } from 'vitest';
+import url from './url.js';
+
+describe('url constants', () => {
+ it('session.detail returns correct path', () => {
+ expect(url.session.detail('abc123')).toBe('/session/abc123');
+ });
+
+ it('message.send returns correct path', () => {
+ expect(url.message.send('sess1')).toBe('/session/sess1/message');
+ });
+});
+```
+
+Run with:
+```bash
+npx vitest run src/renderer/http/url.test.js
+```
+
+### Testing modules that use `window.__opencodeBaseUrl`
+```js
+import { vi, describe, it, expect, beforeEach } from 'vitest';
+
+beforeEach(() => {
+ vi.stubGlobal('__opencodeBaseUrl', 'http://127.0.0.1:5000');
+});
+
+// import and test getBaseUrl() etc.
+```
+
+---
+
+## Code Style
+
+- **Formatter**: Prettier (config in `package.json` defaults). Run `npm run format` to auto-fix, `npm run format:check` for CI check.
+- **Commit messages**: enforced by commitlint (`@commitlint/config-conventional`). Use conventional commits: `feat:`, `fix:`, `chore:`, etc.
+- **Pre-commit hook**: husky + lint-staged runs Prettier on staged `*.js` and `*.vue` files automatically.
+- **No ESLint** is configured — only Prettier for formatting.
+- Vue components use the Composition API (`