diff --git a/AGENTS.md b/AGENTS.md index 64ffec5d..122ab265 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,14 +33,21 @@ After making changes in a specific package, run its check script: - **Bun APIs**: Prefer `Bun.file`, `Bun.serve`, `bun:sqlite`, `Bun.$` over Node equivalents. - **Testing**: Use `bun:test` with `import { test, expect } from "bun:test"`. -## btca +## Better Context MCP -When the user says "use btca" for codebase/docs questions. +Use Better Context MCP for documentation/resource questions when you need source-first answers. -**Available resources**: svelte, svelteKit, tailwind, hono, zod, solidJs, commander, vite, clerk, convexJs, convexWorkpools, daytona +**Required workflow** +1. Call `listResources` first to see available resources. +2. Call `ask` with your question and the exact resource `name` values from step 1. -### Usage +**Rules** +- Always call `listResources` before `ask`. +- `ask` requires at least one resource in the `resources` array. +- Use only resource names returned by `listResources`. +- Include only resources relevant to the question. -```bash -btca ask -r -q "" -``` +**Common errors** +- "Invalid resources" → re-run `listResources` and use exact names. +- "Instance is provisioning / error state" → wait or retry after a minute. +- "Missing or invalid Authorization header" → MCP auth is invalid; fix it in `https://btca.dev/app/settings/mcp/`. \ No newline at end of file diff --git a/INIT_MCP_CLI_REFACTOR_PLAN.md b/INIT_MCP_CLI_REFACTOR_PLAN.md new file mode 100644 index 00000000..f16dd397 --- /dev/null +++ b/INIT_MCP_CLI_REFACTOR_PLAN.md @@ -0,0 +1,157 @@ +# btca init + btca add Refactor Plan + +Date: 2026-01-25 + +## Goals + +- Update `btca init` to offer two setup paths: + - **MCP (cloud hosted resources)** + - **CLI (local resources)** +- For **MCP**, do **not** create `btca.config.jsonc` for now. +- For **CLI**, create `btca.config.jsonc`, update `AGENTS.md`, and update `.gitignore`. +- Provide next-step instructions tailored to the chosen path: + - **MCP:** point user to the MCP dashboard. + - **CLI:** confirm setup is complete and suggest next actions. +- Add a new top-level `btca add` command that: + - Accepts a **GitHub URL only** (e.g. `btca add https://github.com/owner/repo`). + - Prefills fields inferred from GitHub metadata. + - Uses an interactive terminal wizard so the user can confirm/edit each field. + - Writes to **project config** by default, or **global config** with `-g`. + +## Scope Overview + +- **CLI:** `apps/cli` +- No changes to server APIs required (use existing config/resources plumbing). +- `btca init` becomes a guided setup; `btca add` becomes a guided resource wizard. + +--- + +## 1) `btca init` Refactor + +### UX Flow + +1. User runs `btca init` (no flags). +2. Prompt: + - “Choose setup type:” + - `1) MCP (cloud hosted resources)` + - `2) CLI (local resources)` +3. Based on selection: + +#### MCP Path (cloud hosted resources) + +- **Do not** create `btca.config.jsonc` for now. +- Update `AGENTS.md` to include the MCP instructions section. +- Update `.gitignore` (only if needed; no `.btca` for MCP). +- Print next steps: + - “Get your MCP API key from the dashboard: https://btca.dev/app/settings/mcp/” + - “Configure your MCP client with the Better Context endpoint.” + +#### CLI Path (local resources) + +- Create `btca.config.jsonc` with default model/provider + empty `resources`. +- Update `AGENTS.md` with the CLI instructions section (existing btca section or insert). +- Update `.gitignore` if `.btca` local data dir is used (current behavior). +- Print next steps: + - “btca config resources add …” + - “btca ask -r -q …” + - Confirm setup is complete. + +### Implementation Notes + +- **Command file:** `apps/cli/src/commands/init.ts` +- Replace the current `--local` flow with an interactive selection. +- Keep `--force` behavior for config overwrite (CLI path only). +- For MCP path, `--force` should not be necessary (no config file created). +- If a config file already exists: + - CLI path: warn and require `--force` to overwrite. + - MCP path: do not overwrite; still update `AGENTS.md` and show next steps. + +### Output Text + +- Ensure output uses explicit next steps and the MCP dashboard link. +- Add a short “Setup complete” confirmation message for CLI path. + +--- + +## 2) `btca add` Wizard (new command) + +### UX Flow + +Command: + +``` +btca add https://github.com/owner/repo +``` + +Optional global: + +``` +btca add -g https://github.com/owner/repo +``` + +Wizard Steps (prompts should allow edit/confirm): + +1. **URL** (prefilled from arg) +2. **Name** (default = repo name, e.g. `repo`) +3. **Branch** (default = repo default branch; fallback to `main` if unknown) +4. **Search paths** (optional; allow empty) +5. **Notes** (optional) +6. **Confirm summary** → write to config + +### Behavior + +- Accept only GitHub URLs for now; validate and error clearly otherwise. +- Use GitHub URL parsing to infer `owner/repo` and default name. +- Attempt to resolve default branch if possible (if not, default `main`). +- Use existing “add resource” plumbing (current `btca config resources add` flow). +- `-g` writes to `~/.config/btca/btca.config.jsonc` (global) instead of project config. + +### Implementation Notes + +- **New command file:** `apps/cli/src/commands/add.ts` (or inline in `apps/cli/src/index.ts`) +- **Config writing path:** reuse existing config helpers (if present) or create new helper to write config for project/global. +- **Prompting:** use Node readline or existing prompt utilities if available. +- **Validation:** GitHub URL parser should handle: + - `https://github.com/owner/repo` + - `https://github.com/owner/repo.git` + +--- + +## 3) AGENTS.md Updates + +### MCP Section (to insert when MCP path chosen) + +``` +## Better Context MCP + +Use Better Context MCP for documentation/resource questions when you need source‑first answers. + +**Required workflow** +1. Call `listResources` first to see available resources. +2. Call `ask` with your question and the exact resource `name` values from step 1. + +**Rules** +- Always call `listResources` before `ask`. +- `ask` requires at least one resource in the `resources` array. +- Use only resource names returned by `listResources`. +- Include only resources relevant to the question. + +**Common errors** +- “Invalid resources” → re-run `listResources` and use exact names. +- “Instance is provisioning / error state” → wait or retry after a minute. +- “Missing or invalid Authorization header” → MCP auth is invalid; fix it in `https://btca.dev/app/settings/mcp/`. +``` + +### CLI Section + +- Use existing CLI section content (current `AGENTS.md` btca section) or template from `apps/web/src/lib/assets/docs/example-AGENTS-section.md`. +- Ensure it documents `btca ask` usage and where the config lives. + +--- + +## 4) Questions / Follow-ups + +- Should the CLI setup path still support `--local` to use `.btca` data directory, or should it default to global data always? +- For `btca add`, do we want to support multiple search paths via repeated prompt entries or comma-separated input? +- Should `btca add` automatically call `btca init` if no config exists in project path? + diff --git a/ISSUE_IMPLEMENTATION_PLAN.md b/ISSUE_IMPLEMENTATION_PLAN.md deleted file mode 100644 index 0200f37b..00000000 --- a/ISSUE_IMPLEMENTATION_PLAN.md +++ /dev/null @@ -1,1072 +0,0 @@ -# Better Context (btca) - Issue Implementation Plan - -This document outlines the implementation plans for priority bug fixes and features, as well as a roadmap for future work. - ---- - -## Table of Contents - -1. [Priority Fixes - Detailed Plans](#priority-fixes---detailed-plans) - - [Issue #99: Clean up OpenCode instances on app exit](#issue-99-clean-up-opencode-instances-on-app-exit) - - [Issue #76: searchPath validation](#issue-76-searchpath-validation) - - [Issue #81: .btca folder location improvements](#issue-81-btca-folder-location-improvements) - - [Issue #96: Code block scroll on website](#issue-96-code-block-scroll-on-website) -2. [Future Work - Deferred Issues](#future-work---deferred-issues) - - [Issue #109: Model validation for opencode gateway](#issue-109-model-validation-for-opencode-gateway) - - [Issue #105 & #89: Windows Platform Issues](#issue-105--89-windows-platform-issues) - - [Issue #91: Sub-agent mode filtering](#issue-91-sub-agent-mode-filtering) - - [Issue #63: Session resume](#issue-63-session-resume) - - [Issue #108: Input history](#issue-108-input-history) - - [Issue #94, #97, #93: OpenCode Integration Features](#issue-94-97-93-opencode-integration-features) - ---- - -## Priority Fixes - Detailed Plans - ---- - -### Issue #99: Clean up OpenCode instances on app exit - -**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/99 - -**Problem Statement:** -The application creates OpenCode instances (server-side) but doesn't shut them down when the caller exits. This leads to orphaned processes that consume system resources. The issue affects any code path that calls `/opencode`, not just `btca chat`. - -**Current Behavior:** - -- `createOpencodeInstance()` in `apps/server/src/agent/service.ts` creates a server on a random port -- The `getOpencodeInstance()` method explicitly states: "The server stays alive - it's the caller's responsibility to manage the lifecycle" -- No tracking of created instances -- No cleanup endpoint -- No signal handling for graceful shutdown - -**Root Cause Analysis:** - -1. OpenCode servers are created but only closed in the `askStream` flow (line 363: `server.close()`) -2. The `getOpencodeInstance` method (lines 384-404) never closes the server -3. No instance registry exists to track active servers -4. CLI has no signal handlers to clean up on exit - ---- - -#### Implementation Plan - -##### Phase 1: Server-Side Instance Tracking - -**File:** `apps/server/src/agent/service.ts` - -1. **Create an Instance Registry** - - ``` - Location: Top of Agent namespace (after line 14) - - Add: - - Type definition for TrackedInstance: { id: string, server: { close(): void }, createdAt: Date, lastActivity: Date } - - Map to store active instances - - Helper functions: generateInstanceId(), registerInstance(), unregisterInstance() - ``` - -2. **Modify `createOpencodeInstance()` function** - - ``` - Location: Lines 191-225 - - Changes: - - Generate unique instanceId (uuid or nanoid) - - Register the server in the instance map after successful creation - - Return instanceId along with client, server, and baseUrl - ``` - -3. **Update `getOpencodeInstance()` method** - - ``` - Location: Lines 384-404 - - Changes: - - Store the instanceId from createOpencodeInstance - - Return instanceId in the response object alongside url and model - - Update return type to include instanceId - ``` - -4. **Add Instance Management Methods to Service** - - ``` - Location: After line 439 (before the return statement) - - Add new methods: - - closeInstance(instanceId: string): Promise - - Look up instance in registry - - Call server.close() - - Remove from registry - - Log closure - - - listInstances(): { id: string, createdAt: Date, lastActivity: Date }[] - - Return list of active instances - - - closeAllInstances(): Promise<{ closed: number }> - - Iterate through registry - - Close all servers - - Clear registry - - Return count - ``` - -##### Phase 2: HTTP Endpoints - -**File:** `apps/server/src/index.ts` - -1. **Add DELETE endpoint for single instance** - - ``` - Route: DELETE /opencode/:id - - Implementation: - - Extract instanceId from params - - Call agent.closeInstance(instanceId) - - Return 200 with { closed: true } on success - - Return 404 if instance not found - ``` - -2. **Add GET endpoint to list instances** - - ``` - Route: GET /opencode/instances - - Implementation: - - Call agent.listInstances() - - Return 200 with array of instance info - ``` - -3. **Add DELETE endpoint to close all instances** - - ``` - Route: DELETE /opencode/instances - - Implementation: - - Call agent.closeAllInstances() - - Return 200 with { closed: count } - ``` - -##### Phase 3: Client-Side Cleanup - -**File:** `apps/cli/src/client/index.ts` - -1. **Add client method for closing instances** - ``` - Add function: closeOpencodeInstance(baseUrl: string, instanceId: string): Promise - - Make DELETE request to /opencode/:id - - Handle errors appropriately - ``` - -**File:** `apps/cli/src/commands/chat.ts` (or wherever opencode is invoked) - -2. **Implement signal handlers** - ``` - Add at command initialization: - - Track current instanceId when /opencode is called - - Register handlers for: SIGINT, SIGTERM, SIGHUP - - On signal: call closeOpencodeInstance() before exit - - Use try/finally pattern to ensure cleanup - ``` - -**File:** `apps/cli/src/tui/context/messages-context.tsx` - -3. **Add cleanup to TUI context** - ``` - Changes: - - Store active instanceId in context state - - Add cleanup function that closes instance - - Call cleanup on unmount / app exit - ``` - -##### Phase 4: Server-Side Safety Net (Optional but Recommended) - -**File:** `apps/server/src/agent/service.ts` - -1. **Add idle timeout cleanup** - - ``` - Implementation: - - Add configurable IDLE_TIMEOUT_MS constant (default: 5 minutes) - - Create cleanup interval that runs every minute - - Check lastActivity timestamp for each instance - - Close instances that exceed idle timeout - - Log cleanup actions - ``` - -2. **Update lastActivity on instance use** - ``` - Changes: - - When askStream or any method uses an instance, update lastActivity - - This keeps active instances alive while cleaning up forgotten ones - ``` - ---- - -#### Testing Plan - -1. **Unit Tests** - - Test instance registration and unregistration - - Test closeInstance with valid/invalid IDs - - Test closeAllInstances - -2. **Integration Tests** - - Start btca, create instance, verify it's tracked - - Close instance via endpoint, verify it's removed - - Kill CLI process, verify server-side cleanup (via idle timeout) - -3. **Manual Testing** - - Run `btca chat`, start a session, Ctrl+C out - - Use `htop` or `ps` to verify no orphaned processes - - Test with multiple concurrent instances - ---- - -#### Files to Modify - -| File | Changes | -| ----------------------------------------------- | -------------------------------------------- | -| `apps/server/src/agent/service.ts` | Instance registry, tracking, cleanup methods | -| `apps/server/src/agent/types.ts` | New type definitions for tracked instances | -| `apps/server/src/index.ts` | New HTTP endpoints | -| `apps/cli/src/client/index.ts` | Client cleanup method | -| `apps/cli/src/commands/chat.ts` | Signal handlers | -| `apps/cli/src/tui/context/messages-context.tsx` | Cleanup on unmount | - ---- - -#### Acceptance Criteria - -- [x] OpenCode instances are tracked with unique IDs -- [x] `DELETE /opencode/:id` endpoint closes specific instance -- [x] `GET /opencode/instances` lists active instances -- [ ] CLI cleans up instances on SIGINT/SIGTERM (future enhancement) -- [ ] Idle instances are cleaned up after timeout (future enhancement - safety net) -- [x] No orphaned processes after normal or abnormal exit (via API) -- [x] Existing functionality remains unchanged - ---- - -### Issue #76: searchPath validation - -**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/76 - -**Problem Statement:** -When a user configures a resource with an invalid `searchPath` (a path that doesn't exist in the cloned repository), btca doesn't validate this. Instead, it either: - -1. Falls back to searching unrelated directories (like `~/.claude/`) -2. Hallucinates answers based on non-existent content - -This leads to confusing, incorrect responses that appear authoritative. - -**Current Behavior:** - -- Git repos are cloned to `{resourcesDirectory}/{resourceKey}/` -- `searchPath` is used to focus on a subdirectory -- No validation that `searchPath` actually exists after clone -- Collection is created with potentially invalid paths - -**Example from Issue:** - -```json -{ - "searchPath": "tree/dev/packages/web/src/content/docs" // Invalid - "tree/dev" is GitHub UI path -} -``` - -Should have been: - -```json -{ - "searchPath": "packages/web/src/content/docs" // Correct - actual repo path -} -``` - ---- - -#### Implementation Plan - -##### Phase 1: Add searchPath Validation to Git Resource Loading - -**File:** `apps/server/src/resources/impls/git.ts` - -1. **Create validation function** - - ``` - Location: Add new function after clone logic - - Function: validateSearchPaths(repoPath: string, searchPaths: string[]): { valid: string[], invalid: string[] } - - Implementation: - - For each searchPath, check if path exists using Bun.file().exists() or fs.stat() - - Handle both searchPath (single) and searchPaths (array) from config - - Return object with valid and invalid paths - ``` - -2. **Integrate validation after clone** - - ``` - Location: After successful git clone/pull - - Implementation: - - Call validateSearchPaths with the cloned repo path - - If any paths are invalid, throw a descriptive error - - Include: invalid path, repo name, suggestion to check config - ``` - -3. **Create specific error class** - - ``` - Location: Add to errors or within git.ts - - Class: InvalidSearchPathError extends Error - Properties: - - resourceName: string - - invalidPaths: string[] - - repoPath: string - - hint: string (helpful message about common mistakes) - ``` - -##### Phase 2: Improve Error Messages - -**File:** `apps/server/src/resources/impls/git.ts` - -1. **Add helpful hints for common mistakes** - - ``` - Common patterns to detect and suggest fixes: - - - Path starts with "tree/" or "blob/" - → "Remove 'tree/{branch}/' prefix - use the actual repository path" - - - Path starts with "https://" or "github.com" - → "searchPath should be a relative path within the repo, not a URL" - - - Path contains branch name that matches configured branch - → "The branch is already specified in 'branch' field - just use the path after it" - ``` - -2. **Format error message clearly** - - ``` - Example output: - - Error: Invalid searchPath for resource "opencode" - - Path not found: "tree/dev/packages/web/src/content/docs" - Repository: ~/.local/share/btca/resources/opencode - - Hint: It looks like you included the GitHub URL structure. - Remove 'tree/dev/' prefix and use: "packages/web/src/content/docs" - - To see available directories, run: - ls ~/.local/share/btca/resources/opencode - ``` - -##### Phase 3: Validation at Config Time (Optional Enhancement) - -**File:** `apps/server/src/config/index.ts` - -1. **Add validation when adding resources** - - ``` - Location: In addResource method - - Implementation: - - For git resources with searchPath, note that validation will happen on first load - - Consider adding a "validate" flag or separate validation command - - This is optional since full validation requires cloning first - ``` - -**File:** `apps/cli/src/commands/config.ts` - -2. **Add validation command** - - ``` - New command: btca config resources validate - - Implementation: - - Load all git resources - - Clone/update each one - - Validate searchPaths - - Report any issues - - Useful for debugging config issues - ``` - -##### Phase 4: Update Collection Creation - -**File:** `apps/server/src/collections/service.ts` - -1. **Add validation before creating collection** - - ``` - Location: Before symlink creation - - Implementation: - - Verify all resource paths exist - - If searchPath is specified, verify it exists within the resource - - Fail fast with clear error rather than creating partial collection - ``` - ---- - -#### Testing Plan - -1. **Unit Tests** - - Test validateSearchPaths with valid paths - - Test validateSearchPaths with invalid paths - - Test hint generation for common mistakes - - Test error message formatting - -2. **Integration Tests** - - Configure resource with invalid searchPath, verify error - - Configure resource with GitHub URL-style path, verify helpful hint - - Configure resource with valid searchPath, verify success - -3. **Manual Testing** - - Reproduce the exact scenario from the issue - - Verify the error message is helpful - - Test the suggested fix works - ---- - -#### Files to Modify - -| File | Changes | -| ---------------------------------------- | --------------------------------------------- | -| `apps/server/src/resources/impls/git.ts` | Validation function, error class, integration | -| `apps/server/src/collections/service.ts` | Pre-collection validation | -| `apps/server/src/errors.ts` | New error type (optional, could be in git.ts) | -| `apps/cli/src/commands/config.ts` | Optional: validation command | - ---- - -#### Acceptance Criteria - -- [x] Invalid searchPath throws clear error before querying -- [x] Error message includes the invalid path and resource name -- [x] Helpful hints detect common mistakes (GitHub URL patterns, etc.) -- [x] Suggested fix is actionable (shows correct path format) -- [x] Valid searchPaths continue to work as before -- [x] Collection creation fails fast with invalid paths - ---- - -### Issue #81: .btca folder location improvements - -**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/81 - -**Problem Statement:** -When btca creates a `.btca/` folder in a user's project directory, it causes several issues: - -1. `git status` shows `.btca/` as untracked -2. `git add .` triggers embedded repository warnings -3. Users may accidentally commit external repos -4. Each project gets its own clone cache (wasted disk space) - -**Current Behavior:** - -- Global data defaults to `~/.local/share/btca/` -- Project config can set `dataDirectory: ".btca"` which creates local folder -- Legacy migration (lines 709-726 in config) preserves existing `.btca/` folders -- No automatic `.gitignore` management - -**Goal:** - -- Add a `btca init` command for explicit project setup -- Automatically add `.btca/` to `.gitignore` when using local data directory -- Warn users about potential git issues - ---- - -#### Implementation Plan - -##### Phase 1: Add `btca init` Command - -**File:** `apps/cli/src/commands/init.ts` (new file) - -1. **Create new init command** - - ``` - Command: btca init - - Options: - - --local / -l: Use local .btca directory (default: use global) - - --force / -f: Overwrite existing config - - Behavior: - - Check if btca.config.jsonc already exists - - If exists and no --force, warn and exit - - Create btca.config.jsonc with appropriate defaults - - If --local, set dataDirectory: ".btca" - - If --local, update .gitignore - ``` - -2. **Define init command structure** - - ``` - Implementation steps: - - a) Check for existing config - - Look for btca.config.jsonc in cwd - - If exists, prompt or error based on --force - - b) Determine data directory strategy - - Default: inherit from global (no dataDirectory field) - - With --local: set dataDirectory: ".btca" - - c) Create config file - - Use CONFIG_SCHEMA_URL for $schema - - Set reasonable defaults (empty resources array, inherit model/provider) - - Write to btca.config.jsonc - - d) Handle .gitignore (if --local) - - Check if .gitignore exists - - If exists, check if .btca is already listed - - If not listed, append .btca/ entry - - If .gitignore doesn't exist, create with .btca/ entry - - e) Print success message with next steps - ``` - -**File:** `apps/cli/src/index.ts` - -3. **Register the init command** - - ``` - Location: With other command registrations - - Add: - - Import initCommand from './commands/init.ts' - - Register with program.addCommand(initCommand) - ``` - -##### Phase 2: Gitignore Management Utilities - -**File:** `apps/cli/src/lib/utils/gitignore.ts` (new file) - -1. **Create gitignore helper functions** - - ``` - Functions: - - - hasGitignore(dir: string): Promise - - Check if .gitignore exists in directory - - - isPatternInGitignore(dir: string, pattern: string): Promise - - Read .gitignore and check if pattern exists - - Handle comments and variations (.btca, .btca/, .btca/*) - - - addToGitignore(dir: string, pattern: string, comment?: string): Promise - - Append pattern to .gitignore - - Add optional comment above (e.g., "# btca local data") - - Create .gitignore if it doesn't exist - - Ensure newline handling is correct - ``` - -##### Phase 3: Automatic Gitignore Updates - -**File:** `apps/server/src/config/index.ts` - -1. **Add gitignore check on project config creation** - - ``` - Location: In the legacy migration section (lines 709-726) and anywhere project config is created - - Implementation: - - When dataDirectory is set to a relative path (like ".btca") - - Check if we're in a git repository (look for .git folder) - - If yes, check/update .gitignore - - Log a message about the gitignore update - ``` - -2. **Add warning for existing .btca folders** - - ``` - Location: During config load when .btca exists - - Implementation: - - If .btca/ exists and .gitignore doesn't include it - - Log a warning with instructions - - Don't auto-modify in this case (existing behavior preservation) - ``` - -##### Phase 4: Documentation and User Guidance - -**File:** `apps/cli/src/commands/init.ts` - -1. **Add helpful output messages** - - ``` - Success message example: - - Created btca.config.jsonc - - Data directory: .btca/ (local to this project) - Added .btca/ to .gitignore - - Next steps: - 1. Add resources: btca config resources add -n -t git -u - 2. Ask a question: btca ask -r -q "your question" - - Run 'btca --help' for more options. - ``` - -2. **Add warning for non-git directories** - - ``` - If --local but no .git folder: - - Warning: This directory doesn't appear to be a git repository. - The .btca/ folder will be created but .gitignore was not updated. - If you initialize git later, add '.btca/' to your .gitignore. - ``` - ---- - -#### Testing Plan - -1. **Unit Tests** - - Test gitignore helper functions - - Test pattern detection in various .gitignore formats - - Test safe appending with proper newlines - -2. **Integration Tests** - - Run `btca init` in empty directory - - Run `btca init --local` in git repo, verify .gitignore updated - - Run `btca init` with existing config, verify warning - - Run `btca init --force` with existing config, verify overwrite - -3. **Manual Testing** - - Full workflow: init, add resource, ask question - - Verify `git status` doesn't show .btca/ - - Verify no embedded repo warnings on `git add .` - ---- - -#### Files to Modify/Create - -| File | Changes | -| ------------------------------------- | ------------------------------ | -| `apps/cli/src/commands/init.ts` | New file - init command | -| `apps/cli/src/lib/utils/gitignore.ts` | New file - gitignore utilities | -| `apps/cli/src/index.ts` | Register init command | -| `apps/server/src/config/index.ts` | Gitignore warnings/auto-update | - ---- - -#### Acceptance Criteria - -- [x] `btca init` creates project config file -- [x] `btca init --local` sets up local .btca directory -- [x] .gitignore is updated when using local data directory in git repo -- [x] Existing .gitignore entries are preserved -- [x] Warning shown if .btca exists but not in .gitignore (warning shown for non-git repos) -- [x] Clear success messages with next steps -- [x] `--force` flag allows overwriting existing config - ---- - -### Issue #96: Code block scroll on website - -**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/96 - -**Problem Statement:** -Code blocks on the btca.dev homepage are truncated horizontally without a scrollbar, forcing users to manually select and drag text to see the full content. - -**Current Behavior:** - -- Code blocks overflow their container -- No horizontal scrollbar visible -- Content is cut off on the right side - -**Affected File:** `apps/web/src/routes/+page.svelte` - ---- - -#### Implementation Plan - -##### Phase 1: Identify Affected Elements - -**File:** `apps/web/src/routes/+page.svelte` - -1. **Audit code block rendering** - ``` - Investigation: - - Find all
 and  elements
-   - Check if using a syntax highlighting library (e.g., Prism, Shiki)
-   - Identify the CSS classes applied to code blocks
-   - Check parent container constraints
-   ```
-
-##### Phase 2: CSS Fixes
-
-**File:** `apps/web/src/routes/+page.svelte` or global CSS file
-
-1. **Add overflow handling to code blocks**
-
-   ```css
-   /* Option A: Direct element styling */
-   pre {
-   	overflow-x: auto;
-   	max-width: 100%;
-   }
-
-   pre code {
-   	display: block;
-   	overflow-x: auto;
-   }
-
-   /* Option B: If using Tailwind */
-   /* Add classes: overflow-x-auto max-w-full */
-   ```
-
-2. **Ensure proper container constraints**
-
-   ```css
-   /* Parent container should constrain width */
-   .code-container {
-   	max-width: 100%;
-   	overflow: hidden;
-   }
-
-   /* Code block itself handles scroll */
-   .code-container pre {
-   	overflow-x: auto;
-   	scrollbar-width: thin; /* Firefox */
-   }
-
-   /* Webkit scrollbar styling (optional) */
-   .code-container pre::-webkit-scrollbar {
-   	height: 8px;
-   }
-
-   .code-container pre::-webkit-scrollbar-thumb {
-   	background-color: rgba(0, 0, 0, 0.2);
-   	border-radius: 4px;
-   }
-   ```
-
-##### Phase 3: Test Across Breakpoints
-
-1. **Responsive testing**
-
-   ```
-   Test at:
-   - Desktop (1920px, 1440px, 1280px)
-   - Tablet (1024px, 768px)
-   - Mobile (425px, 375px, 320px)
-
-   Verify:
-   - Scrollbar appears when content overflows
-   - Scrollbar is usable (not too thin)
-   - No horizontal page scroll (only code block scrolls)
-   ```
-
-2. **Browser testing**
-
-   ```
-   Test in:
-   - Chrome/Edge (Chromium)
-   - Firefox
-   - Safari
-
-   Verify scrollbar styling works or degrades gracefully
-   ```
-
----
-
-#### Files to Modify
-
-| File                               | Changes                 |
-| ---------------------------------- | ----------------------- |
-| `apps/web/src/routes/+page.svelte` | CSS for code blocks     |
-| `apps/web/src/app.css`             | Global styles if needed |
-
----
-
-#### Acceptance Criteria
-
-- [x] Code blocks show horizontal scrollbar when content overflows
-- [x] Scrollbar is visible and usable
-- [x] No content is cut off without indication
-- [x] Page doesn't scroll horizontally (only code block)
-- [x] Works across major browsers
-- [x] Responsive across screen sizes
-
----
-
-## Future Work - Deferred Issues
-
-This section documents issues that are deferred for now but should be addressed in future development cycles.
-
----
-
-### Issue #109: Model validation for opencode gateway
-
-**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/109
-
-**Problem Summary:**
-When using `provider: "opencode"` with a model like `claude-haiku-4-5`, btca rejects the model because opencode acts as a gateway/router to other providers, but validation only checks against opencode's native model list.
-
-**Impact:** Medium-High - Blocks users who want to use opencode as a unified gateway
-
-**High-Level Solution:**
-
-1. Detect when provider is a "gateway" provider (like opencode)
-2. Either skip model validation entirely for gateway providers
-3. Or fetch the full list of routable models from the gateway
-4. Update `validateProviderAndModel()` in `apps/server/src/agent/service.ts`
-
-**Estimated Effort:** Small (1-2 hours)
-
-**Key Files:**
-
-- `apps/server/src/agent/service.ts` (lines 166-189)
-
----
-
-### Issue #105 & #89: Windows Platform Issues
-
-**GitHub Issues:**
-
-- https://github.com/davis7dotsh/better-context/issues/105
-- https://github.com/davis7dotsh/better-context/issues/89
-
-**Problem Summary:**
-
-- #105: TUI crashes with "Orphan text error" from OpenTUI on Windows 11
-- #89: btca gets stuck at "creating collection..." on Windows 11/PowerShell
-
-Both issues indicate Windows is not a well-supported platform currently.
-
-**Impact:** High - Completely blocks Windows users
-
-**High-Level Solution:**
-
-1. **Investigation Phase:**
-   - Set up Windows development environment
-   - Reproduce both issues
-   - Determine if issues are in OpenTUI, Bun, or btca code
-
-2. **Short-term Mitigations:**
-   - Add `--no-tui` flag to bypass TUI entirely
-   - Add better error handling and timeout messages
-   - Document WSL as recommended approach for Windows
-
-3. **Long-term Fixes:**
-   - Work with OpenTUI maintainers if upstream issue
-   - Add Windows-specific code paths if needed
-   - Consider alternative TUI library with better Windows support
-
-**Estimated Effort:** Large (1-2 weeks for proper Windows support)
-
-**Key Files:**
-
-- `apps/cli/src/tui/` (entire directory)
-- `apps/cli/src/commands/tui.ts`
-- Potentially OpenTUI library (external)
-
----
-
-### Issue #91: Sub-agent mode filtering
-
-**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/91
-
-**Problem Summary:**
-When btca is used as a sub-agent in an agentic workflow, the LLM's reasoning traces and tool calls pollute the primary agent's context, increasing token costs and making outputs harder to parse.
-
-**Impact:** Medium - Important for agentic workflows
-
-**High-Level Solution:**
-
-1. Add CLI flags: `--hide-reasoning`, `--hide-tool-calls`, or `--subagent-mode`
-2. Filter stream events before output based on flags
-3. Use existing `packages/shared/src/stream-filter.ts` utilities
-4. Default: show everything; subagent mode: final answer only
-
-**Estimated Effort:** Medium (4-8 hours)
-
-**Key Files:**
-
-- `apps/cli/src/commands/ask.ts`
-- `packages/shared/src/stream-filter.ts`
-- `apps/server/src/agent/service.ts` (event handling)
-
----
-
-### Issue #63: Session resume
-
-**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/63
-
-**Problem Summary:**
-Users cannot list or resume previous `btca chat` sessions. Each session is ephemeral and lost when the CLI exits.
-
-**Impact:** Medium - Quality of life improvement
-
-**High-Level Solution:**
-
-1. Add session persistence to `~/.local/share/btca/sessions/`
-2. Store: session ID, messages, resources, timestamps
-3. Add commands: `btca sessions list`, `btca sessions resume `
-4. Port schema concepts from web app's Convex implementation
-
-**Estimated Effort:** Medium-Large (8-16 hours)
-
-**Key Files:**
-
-- `apps/cli/src/tui/context/messages-context.tsx`
-- `apps/cli/src/commands/` (new sessions command)
-- `apps/web/src/convex/threads.ts` (reference implementation)
-
----
-
-### Issue #108: Input history
-
-**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/108
-
-**Problem Summary:**
-Users cannot navigate through previous inputs using up/down arrows like standard terminal history.
-
-**Impact:** Low-Medium - Nice UX improvement
-
-**High-Level Solution:**
-
-1. Add history file at `~/.local/share/btca/history.json`
-2. Store last N inputs (configurable, default 100)
-3. Implement up/down arrow navigation in TUI input component
-4. Persist across sessions
-
-**Estimated Effort:** Small-Medium (2-4 hours)
-
-**Key Files:**
-
-- `apps/cli/src/tui/components/` (input component)
-- New history utility file
-
----
-
-### Issue #94, #97, #93: OpenCode Integration Features
-
-**GitHub Issues:**
-
-- https://github.com/davis7dotsh/better-context/issues/94 (btca opencode tool/plugin)
-- https://github.com/davis7dotsh/better-context/issues/97 (custom tool configuration)
-- https://github.com/davis7dotsh/better-context/issues/93 (external file read allowlist)
-
-**Problem Summary:**
-These three issues relate to deeper OpenCode integration:
-
-- #94: Create official btca tool for OpenCode agents
-- #97: Allow users to enable/disable custom tools in btca's OpenCode instance
-- #93: Allow btca to read files outside the collection (e.g., config files)
-
-**Impact:** Medium - Power user features
-
-**High-Level Solutions:**
-
-**#94 - btca OpenCode tool:**
-
-1. Create tool script template at `.config/opencode/tools/btca.ts`
-2. Implement actions: list, ask, add
-3. Document integration patterns
-
-**#97 - Custom tool configuration:**
-
-1. Add `opencode.tools` and `opencode.permission` to config schema
-2. Merge user config into `buildOpenCodeConfig()`
-3. Add CLI flags for ephemeral overrides
-
-**#93 - External file allowlist:**
-
-1. Add `externalReadAllowlist` config option
-2. Modify permission handling in agent config
-3. Default deny, explicit allowlist for security
-
-**Estimated Effort:** Medium (4-8 hours each)
-
-**Key Files:**
-
-- `apps/server/src/agent/service.ts`
-- `apps/server/src/config/index.ts`
-- Config schema files
-
----
-
-## Issue #101: Missing linux-arm64 binary
-
-**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/101
-
-**Problem Summary:**
-btca fails to run in Alpine Linux ARM64 Docker containers because the `btca-linux-arm64` binary is not included in the distribution.
-
-**Impact:** Medium - Blocks containerized deployments on ARM64
-
-**High-Level Solution:**
-
-1. Update build scripts to include ARM64 Linux target
-2. Or: Document that Alpine (musl) is not supported, recommend glibc-based images
-3. Or: Provide instructions for building from source in containers
-
-**Estimated Effort:** Small (1-2 hours if just build config)
-
-**Key Files:**
-
-- `package.json` (build scripts)
-- Build/release configuration
-
----
-
-## Issue #90: Windows local file paths
-
-**GitHub Issue:** https://github.com/davis7dotsh/better-context/issues/90
-
-**Problem Summary:**
-Local file paths don't work correctly on Windows (e.g., `E:\GitHub\...`).
-
-**Impact:** Medium - Blocks Windows users with local resources
-
-**High-Level Solution:**
-
-1. Normalize paths using `path.normalize()` and `path.resolve()`
-2. Handle Windows drive letters and backslashes
-3. Test local resource configuration on Windows
-
-**Estimated Effort:** Small-Medium (2-4 hours)
-
-**Key Files:**
-
-- `apps/server/src/config/index.ts`
-- `apps/server/src/resources/impls/local.ts`
-
----
-
-## Implementation Priority Matrix
-
-| Issue                   | Effort | Impact | Priority Score | Status   |
-| ----------------------- | ------ | ------ | -------------- | -------- |
-| #99 (cleanup)           | Medium | High   | **High**       | DONE     |
-| #76 (searchPath)        | Medium | High   | **High**       | DONE     |
-| #81 (.btca folder)      | Medium | Medium | **Medium**     | DONE     |
-| #96 (CSS scroll)        | Small  | Low    | **Low**        | DONE     |
-| #109 (gateway)          | Small  | High   | High           | DEFERRED |
-| #105/#89 (Windows)      | Large  | High   | High           | DEFERRED |
-| #91 (sub-agent)         | Medium | Medium | Medium         | DEFERRED |
-| #63 (sessions)          | Large  | Medium | Medium         | DEFERRED |
-| #108 (history)          | Small  | Low    | Low            | DEFERRED |
-| #94/97/93 (OC features) | Medium | Medium | Medium         | DEFERRED |
-
----
-
-## Next Steps
-
-1. Begin implementation of Issue #99 (OpenCode instance cleanup)
-2. Follow with Issue #76 (searchPath validation)
-3. Then Issue #81 (.btca folder / btca init)
-4. Finally Issue #96 (CSS fix - quick win)
-
-Each implementation should include:
-
-- Code changes as specified
-- Unit/integration tests
-- Documentation updates if needed
-- PR with reference to the GitHub issue
diff --git a/apps/cli/src/commands/add.ts b/apps/cli/src/commands/add.ts
new file mode 100644
index 00000000..e47a2d6f
--- /dev/null
+++ b/apps/cli/src/commands/add.ts
@@ -0,0 +1,262 @@
+import { Command } from 'commander';
+import * as readline from 'readline';
+import path from 'node:path';
+import os from 'node:os';
+import { promises as fs } from 'node:fs';
+import { ensureServer } from '../server/manager.ts';
+import { addResource, BtcaError } from '../client/index.ts';
+import { dim } from '../lib/utils/colors.ts';
+
+const PROJECT_CONFIG_FILENAME = 'btca.config.jsonc';
+const GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.config', 'btca');
+const GLOBAL_CONFIG_PATH = path.join(GLOBAL_CONFIG_DIR, PROJECT_CONFIG_FILENAME);
+
+interface GitHubUrlParts {
+	owner: string;
+	repo: string;
+}
+
+/**
+ * Parse a GitHub URL and extract owner/repo.
+ */
+function parseGitHubUrl(url: string): GitHubUrlParts | null {
+	// Handle various GitHub URL formats:
+	// - https://github.com/owner/repo
+	// - https://github.com/owner/repo.git
+	// - github.com/owner/repo
+	const patterns = [
+		/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/,
+		/^github\.com\/([^/]+)\/([^/]+?)(\.git)?$/
+	];
+
+	for (const pattern of patterns) {
+		const match = url.match(pattern);
+		if (match) {
+			return {
+				owner: match[1]!,
+				repo: match[2]!
+			};
+		}
+	}
+
+	return null;
+}
+
+/**
+ * Normalize GitHub URL to standard format.
+ */
+function normalizeGitHubUrl(url: string): string {
+	const parts = parseGitHubUrl(url);
+	if (!parts) return url;
+	return `https://github.com/${parts.owner}/${parts.repo}`;
+}
+
+/**
+ * Check if a file exists.
+ */
+async function fileExists(filePath: string): Promise {
+	try {
+		await fs.access(filePath);
+		return true;
+	} catch {
+		return false;
+	}
+}
+
+/**
+ * Get the config path based on -g flag.
+ */
+function getConfigPath(global: boolean): string {
+	if (global) {
+		return GLOBAL_CONFIG_PATH;
+	}
+	return path.join(process.cwd(), PROJECT_CONFIG_FILENAME);
+}
+
+/**
+ * Format an error for display, including hint if available.
+ */
+function formatError(error: unknown): string {
+	if (error instanceof BtcaError) {
+		let output = `Error: ${error.message}`;
+		if (error.hint) {
+			output += `\n\nHint: ${error.hint}`;
+		}
+		return output;
+	}
+	return `Error: ${error instanceof Error ? error.message : String(error)}`;
+}
+
+/**
+ * Create a readline interface for prompts.
+ */
+function createRl(): readline.Interface {
+	return readline.createInterface({
+		input: process.stdin,
+		output: process.stdout
+	});
+}
+
+/**
+ * Prompt for input with a default value.
+ */
+async function promptInput(
+	rl: readline.Interface,
+	question: string,
+	defaultValue?: string
+): Promise {
+	return new Promise((resolve) => {
+		const defaultHint = defaultValue ? ` ${dim(`(${defaultValue})`)}` : '';
+		rl.question(`${question}${defaultHint}: `, (answer) => {
+			const value = answer.trim();
+			resolve(value || defaultValue || '');
+		});
+	});
+}
+
+/**
+ * Prompt for confirmation (y/n).
+ */
+async function promptConfirm(rl: readline.Interface, question: string): Promise {
+	return new Promise((resolve) => {
+		rl.question(`${question} ${dim('(y/n)')}: `, (answer) => {
+			resolve(answer.trim().toLowerCase() === 'y');
+		});
+	});
+}
+
+/**
+ * Prompt for repeated entries (search paths).
+ */
+async function promptRepeated(rl: readline.Interface, itemName: string): Promise {
+	const items: string[] = [];
+
+	console.log(`\nEnter ${itemName} one at a time. Press Enter with empty input when done.`);
+
+	while (true) {
+		const value = await promptInput(rl, `  ${itemName} ${items.length + 1}`);
+		if (!value) break;
+		items.push(value);
+	}
+
+	return items;
+}
+
+export const addCommand = new Command('add')
+	.description('Add a GitHub repository as a resource')
+	.argument('', 'GitHub repository URL (e.g., https://github.com/owner/repo)')
+	.option('-g, --global', 'Add to global config instead of project config')
+	.action(async (url: string, options: { global?: boolean }, command) => {
+		const globalOpts = command.parent?.opts() as { server?: string; port?: number } | undefined;
+		const configPath = getConfigPath(options.global ?? false);
+
+		try {
+			// Validate GitHub URL
+			const urlParts = parseGitHubUrl(url);
+			if (!urlParts) {
+				console.error('Error: Invalid GitHub URL.');
+				console.error('Expected format: https://github.com/owner/repo');
+				process.exit(1);
+			}
+
+			// Check if config exists
+			if (!(await fileExists(configPath))) {
+				if (options.global) {
+					console.error(`Error: Global config not found at ${GLOBAL_CONFIG_PATH}`);
+					console.error('Run "btca init" first to create a configuration.');
+				} else {
+					console.error(`Error: No ${PROJECT_CONFIG_FILENAME} found in current directory.`);
+					console.error('Run "btca init" first to create a project configuration.');
+				}
+				process.exit(1);
+			}
+
+			const normalizedUrl = normalizeGitHubUrl(url);
+
+			// Start the interactive wizard
+			console.log('\n--- Add Resource Wizard ---\n');
+			console.log(`Repository: ${normalizedUrl}`);
+
+			const rl = createRl();
+
+			try {
+				// Step 1: URL (prefilled, confirm)
+				const finalUrl = await promptInput(rl, 'URL', normalizedUrl);
+
+				// Step 2: Name (default = repo name)
+				const defaultName = urlParts.repo;
+				const name = await promptInput(rl, 'Name', defaultName);
+
+				// Step 3: Branch (default = main)
+				const branch = await promptInput(rl, 'Branch', 'main');
+
+				// Step 4: Search paths (optional, repeated)
+				const wantSearchPaths = await promptConfirm(
+					rl,
+					'Do you want to add search paths (subdirectories to focus on)?'
+				);
+				const searchPaths = wantSearchPaths ? await promptRepeated(rl, 'Search path') : [];
+
+				// Step 5: Notes (optional)
+				const notes = await promptInput(rl, 'Notes (optional)');
+
+				rl.close();
+
+				// Summary
+				console.log('\n--- Summary ---\n');
+				console.log(`  Name:    ${name}`);
+				console.log(`  URL:     ${finalUrl}`);
+				console.log(`  Branch:  ${branch}`);
+				if (searchPaths.length > 0) {
+					console.log(`  Search:  ${searchPaths.join(', ')}`);
+				}
+				if (notes) {
+					console.log(`  Notes:   ${notes}`);
+				}
+				console.log(`  Config:  ${options.global ? 'global' : 'project'}`);
+				console.log('');
+
+				// Confirm
+				const confirmRl = createRl();
+				const confirmed = await promptConfirm(confirmRl, 'Add this resource?');
+				confirmRl.close();
+
+				if (!confirmed) {
+					console.log('\nCancelled.');
+					process.exit(0);
+				}
+
+				// Add the resource via server
+				const server = await ensureServer({
+					serverUrl: globalOpts?.server,
+					port: globalOpts?.port,
+					quiet: true
+				});
+
+				const resource = await addResource(server.url, {
+					type: 'git',
+					name,
+					url: finalUrl,
+					branch,
+					...(searchPaths.length === 1 && { searchPath: searchPaths[0] }),
+					...(searchPaths.length > 1 && { searchPaths }),
+					...(notes && { specialNotes: notes })
+				});
+
+				server.stop();
+
+				console.log(`\nAdded resource: ${name}`);
+				if (resource.type === 'git' && resource.url !== finalUrl) {
+					console.log(`  URL normalized: ${resource.url}`);
+				}
+				console.log('\nYou can now use this resource:');
+				console.log(`  btca ask -r ${name} -q "your question"`);
+			} catch (error) {
+				rl.close();
+				throw error;
+			}
+		} catch (error) {
+			console.error(formatError(error));
+			process.exit(1);
+		}
+	});
diff --git a/apps/cli/src/commands/init.ts b/apps/cli/src/commands/init.ts
index 66274c6b..05fc454c 100644
--- a/apps/cli/src/commands/init.ts
+++ b/apps/cli/src/commands/init.ts
@@ -1,22 +1,118 @@
 import { Command } from 'commander';
 import { promises as fs } from 'node:fs';
 import path from 'node:path';
+import * as readline from 'readline';
 
 const PROJECT_CONFIG_FILENAME = 'btca.config.jsonc';
-const CONFIG_SCHEMA_URL = 'https://btca.dev/btca.schema.json';
+const CONFIG_SCHEMA_URL = 'https://btca.schema.json';
 const DEFAULT_MODEL = 'claude-haiku-4-5';
 const DEFAULT_PROVIDER = 'opencode';
+const MCP_DASHBOARD_URL = 'https://btca.dev/app/settings/mcp/';
+
+// AGENTS.md section templates
+const MCP_AGENTS_SECTION = `## Better Context MCP
+
+Use Better Context MCP for documentation/resource questions when you need source-first answers.
+
+**Required workflow**
+1. Call \`listResources\` first to see available resources.
+2. Call \`ask\` with your question and the exact resource \`name\` values from step 1.
+
+**Rules**
+- Always call \`listResources\` before \`ask\`.
+- \`ask\` requires at least one resource in the \`resources\` array.
+- Use only resource names returned by \`listResources\`.
+- Include only resources relevant to the question.
+
+**Common errors**
+- "Invalid resources" → re-run \`listResources\` and use exact names.
+- "Instance is provisioning / error state" → wait or retry after a minute.
+- "Missing or invalid Authorization header" → MCP auth is invalid; fix it in \`https://btca.dev/app/settings/mcp/\`.
+`;
+
+const CLI_AGENTS_SECTION = `## btca
+
+When you need up-to-date information about technologies used in this project, use btca to query source repositories directly.
+
+**Available resources**: (run \`btca config resources list\` to see configured resources)
+
+### Usage
+
+Ask a question about one or more resources:
+
+\`\`\`bash
+btca ask --resource  --question ""
+\`\`\`
+
+Examples:
+
+\`\`\`bash
+# Single resource
+btca ask --resource svelte --question "How do stores work in Svelte 5?"
+
+# Multiple resources
+btca ask --resource svelte --resource effect --question "How do I integrate Effect with Svelte?"
+
+# Using @mentions in the question
+btca ask --question "@svelte @tailwind How do I style components?"
+\`\`\`
+
+### Interactive Mode
+
+Start a chat session for deeper exploration:
+
+\`\`\`bash
+btca chat --resource svelte --resource effect
+\`\`\`
+
+Or use the TUI:
+
+\`\`\`bash
+btca
+\`\`\`
+
+Then use \`@mentions\` to reference resources (e.g., "@svelte How do I create a store?")
+
+### Configuration
+
+This project's btca resources are configured in \`btca.config.jsonc\` at the project root. To modify:
+
+- Edit the config file directly, or
+- Use \`btca config resources add/remove\` commands
+`;
+
+type SetupType = 'mcp' | 'cli';
+type StorageType = 'local' | 'global';
 
 /**
- * Check if a .gitignore file exists in the given directory.
+ * Prompt user for single selection.
  */
-async function hasGitignore(dir: string): Promise {
-	try {
-		await fs.access(path.join(dir, '.gitignore'));
-		return true;
-	} catch {
-		return false;
-	}
+async function promptSelect(
+	question: string,
+	options: { label: string; value: T }[]
+): Promise {
+	return new Promise((resolve, reject) => {
+		const rl = readline.createInterface({
+			input: process.stdin,
+			output: process.stdout
+		});
+
+		console.log(`\n${question}\n`);
+		options.forEach((opt, idx) => {
+			console.log(`  ${idx + 1}) ${opt.label}`);
+		});
+		console.log('');
+
+		rl.question('Enter number: ', (answer) => {
+			rl.close();
+			const num = parseInt(answer.trim(), 10);
+			if (isNaN(num) || num < 1 || num > options.length) {
+				reject(new Error('Invalid selection'));
+				return;
+			}
+			resolve(options[num - 1]!.value);
+		});
+	});
 }
 
 /**
@@ -92,74 +188,162 @@ async function fileExists(filePath: string): Promise {
 	}
 }
 
+/**
+ * Update or create AGENTS.md with the appropriate section.
+ */
+async function updateAgentsMd(dir: string, section: string): Promise {
+	const agentsPath = path.join(dir, 'AGENTS.md');
+
+	try {
+		let content = await fs.readFile(agentsPath, 'utf-8');
+
+		// Check if already has a btca/Better Context section
+		const btcaRegex = /## btca[\s\S]*?(?=\n## |\n# |$)/;
+		const mcpRegex = /## Better Context MCP[\s\S]*?(?=\n## |\n# |$)/;
+		const betterContextRegex = /## Better Context[\s\S]*?(?=\n## |\n# |$)/;
+
+		if (btcaRegex.test(content)) {
+			content = content.replace(btcaRegex, section.trim());
+		} else if (mcpRegex.test(content)) {
+			content = content.replace(mcpRegex, section.trim());
+		} else if (betterContextRegex.test(content)) {
+			content = content.replace(betterContextRegex, section.trim());
+		} else {
+			// Append to end
+			if (!content.endsWith('\n')) {
+				content += '\n';
+			}
+			content += '\n' + section;
+		}
+
+		await fs.writeFile(agentsPath, content, 'utf-8');
+	} catch {
+		// File doesn't exist, create it
+		await fs.writeFile(agentsPath, `# AGENTS.md\n\n${section}`, 'utf-8');
+	}
+}
+
 export const initCommand = new Command('init')
-	.description('Initialize a btca project configuration')
-	.option('-l, --local', 'Use local .btca directory for data storage')
+	.description('Initialize btca for this project')
 	.option('-f, --force', 'Overwrite existing configuration')
-	.action(async (options: { local?: boolean; force?: boolean }) => {
+	.action(async (options: { force?: boolean }) => {
 		const cwd = process.cwd();
 		const configPath = path.join(cwd, PROJECT_CONFIG_FILENAME);
 
 		try {
-			// Check if config already exists
-			if (await fileExists(configPath)) {
-				if (!options.force) {
-					console.error(`Error: ${PROJECT_CONFIG_FILENAME} already exists.`);
-					console.error('Use --force to overwrite.');
-					process.exit(1);
-				}
-				console.log(`Overwriting existing ${PROJECT_CONFIG_FILENAME}...`);
-			}
+			// Step 1: Ask for setup type
+			const setupType = await promptSelect('Choose setup type:', [
+				{ label: 'MCP (cloud hosted resources)', value: 'mcp' },
+				{ label: 'CLI (local resources)', value: 'cli' }
+			]);
 
-			// Build the config
-			const config: Record = {
-				$schema: CONFIG_SCHEMA_URL,
-				model: DEFAULT_MODEL,
-				provider: DEFAULT_PROVIDER,
-				resources: []
-			};
-
-			// Add dataDirectory if --local flag is used
-			if (options.local) {
-				config.dataDirectory = '.btca';
+			if (setupType === 'mcp') {
+				// MCP Path
+				await handleMcpSetup(cwd);
+			} else {
+				// CLI Path
+				await handleCliSetup(cwd, configPath, options.force);
 			}
+		} catch (error) {
+			if (error instanceof Error && error.message === 'Invalid selection') {
+				console.error('\nError: Invalid selection. Please run btca init again.');
+				process.exit(1);
+			}
+			console.error('Error:', error instanceof Error ? error.message : String(error));
+			process.exit(1);
+		}
+	});
 
-			// Write config file
-			const configContent = JSON.stringify(config, null, '\t');
-			await fs.writeFile(configPath, configContent, 'utf-8');
+/**
+ * Handle MCP setup path.
+ */
+async function handleMcpSetup(cwd: string): Promise {
+	// Update AGENTS.md with MCP section
+	await updateAgentsMd(cwd, MCP_AGENTS_SECTION);
+	console.log('\nUpdated AGENTS.md with Better Context MCP instructions.');
 
-			console.log(`Created ${PROJECT_CONFIG_FILENAME}`);
+	// Print next steps
+	console.log('\n--- Setup Complete (MCP) ---\n');
+	console.log('Next steps:');
+	console.log(`  1. Get your MCP API key from the dashboard: ${MCP_DASHBOARD_URL}`);
+	console.log('  2. Configure your MCP client with the Better Context endpoint.');
+	console.log('\nSee the dashboard for detailed setup instructions.');
+}
 
-			// Handle .gitignore if using local data directory
-			if (options.local) {
-				const inGitRepo = await isGitRepo(cwd);
+/**
+ * Handle CLI setup path.
+ */
+async function handleCliSetup(cwd: string, configPath: string, force?: boolean): Promise {
+	// Check if config already exists
+	if (await fileExists(configPath)) {
+		if (!force) {
+			console.error(`\nError: ${PROJECT_CONFIG_FILENAME} already exists.`);
+			console.error('Use --force to overwrite.');
+			process.exit(1);
+		}
+		console.log(`\nOverwriting existing ${PROJECT_CONFIG_FILENAME}...`);
+	}
+
+	// Ask for storage type
+	const storageType = await promptSelect('Where should btca store cloned resources?', [
+		{ label: 'Local (.btca/ in this project)', value: 'local' },
+		{ label: 'Global (~/.local/share/btca/)', value: 'global' }
+	]);
 
-				if (inGitRepo) {
-					const alreadyIgnored = await isPatternInGitignore(cwd, '.btca');
+	// Build the config
+	const config: Record = {
+		$schema: CONFIG_SCHEMA_URL,
+		model: DEFAULT_MODEL,
+		provider: DEFAULT_PROVIDER,
+		resources: []
+	};
 
-					if (!alreadyIgnored) {
-						await addToGitignore(cwd, '.btca/', '# btca local data');
-						console.log('Added .btca/ to .gitignore');
-					} else {
-						console.log('.btca/ already in .gitignore');
-					}
-				} else {
-					console.log("\nWarning: This directory doesn't appear to be a git repository.");
-					console.log('The .btca/ folder will be created but .gitignore was not updated.');
-					console.log("If you initialize git later, add '.btca/' to your .gitignore.");
-				}
+	// Add dataDirectory if local storage chosen
+	if (storageType === 'local') {
+		config.dataDirectory = '.btca';
+	}
+
+	// Write config file
+	const configContent = JSON.stringify(config, null, '\t');
+	await fs.writeFile(configPath, configContent, 'utf-8');
+	console.log(`\nCreated ${PROJECT_CONFIG_FILENAME}`);
+
+	// Handle .gitignore if using local data directory
+	if (storageType === 'local') {
+		const inGitRepo = await isGitRepo(cwd);
 
-				console.log('\nData directory: .btca/ (local to this project)');
+		if (inGitRepo) {
+			const alreadyIgnored = await isPatternInGitignore(cwd, '.btca');
+
+			if (!alreadyIgnored) {
+				await addToGitignore(cwd, '.btca/', '# btca local data');
+				console.log('Added .btca/ to .gitignore');
 			} else {
-				console.log('\nData directory: ~/.local/share/btca/ (global)');
+				console.log('.btca/ already in .gitignore');
 			}
-
-			console.log('\nNext steps:');
-			console.log('  1. Add resources: btca config resources add -n  -t git -u ');
-			console.log('  2. Ask a question: btca ask -r  -q "your question"');
-			console.log("\nRun 'btca --help' for more options.");
-		} catch (error) {
-			console.error('Error:', error instanceof Error ? error.message : String(error));
-			process.exit(1);
+		} else {
+			console.log("\nWarning: This directory doesn't appear to be a git repository.");
+			console.log('The .btca/ folder will be created but .gitignore was not updated.');
+			console.log("If you initialize git later, add '.btca/' to your .gitignore.");
 		}
-	});
+	}
+
+	// Update AGENTS.md with CLI section
+	await updateAgentsMd(cwd, CLI_AGENTS_SECTION);
+	console.log('Updated AGENTS.md with btca CLI instructions.');
+
+	// Print summary
+	if (storageType === 'local') {
+		console.log('\nData directory: .btca/ (local to this project)');
+	} else {
+		console.log('\nData directory: ~/.local/share/btca/ (global)');
+	}
+
+	// Print next steps
+	console.log('\n--- Setup Complete (CLI) ---\n');
+	console.log('Next steps:');
+	console.log('  1. Add resources: btca add https://github.com/owner/repo');
+	console.log('     Or: btca config resources add -n  -t git -u ');
+	console.log('  2. Ask a question: btca ask -r  -q "your question"');
+	console.log("\nRun 'btca --help' for more options.");
+}
diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts
index 081e3cbb..f4f001a8 100644
--- a/apps/cli/src/index.ts
+++ b/apps/cli/src/index.ts
@@ -1,4 +1,5 @@
 import { Command } from 'commander';
+import { addCommand } from './commands/add.ts';
 import { askCommand } from './commands/ask.ts';
 import { chatCommand } from './commands/chat.ts';
 import { configCommand } from './commands/config.ts';
@@ -27,6 +28,7 @@ const program = new Command()
 		'Use simple REPL mode instead of TUI (useful for Windows or minimal terminals)'
 	);
 
+program.addCommand(addCommand);
 program.addCommand(askCommand);
 program.addCommand(chatCommand);
 program.addCommand(configCommand);
diff --git a/btca.config.jsonc b/btca.config.jsonc
index 75386696..76d2897d 100644
--- a/btca.config.jsonc
+++ b/btca.config.jsonc
@@ -1,5 +1,6 @@
 {
 	"$schema": "https://btca.dev/btca.schema.json",
+	"dataDirectory": ".btca",
 	"resources": [
 		{
 			"type": "git",
@@ -119,6 +120,5 @@
 		}
 	],
 	"model": "claude-haiku-4-5",
-	"provider": "opencode",
-	"dataDirectory": ".btca"
+	"provider": "opencode"
 }