Skip to content

Add multi user support#120

Open
joeldierkes wants to merge 10 commits intomainfrom
vk/f5b2-add-multi-user-s
Open

Add multi user support#120
joeldierkes wants to merge 10 commits intomainfrom
vk/f5b2-add-multi-user-s

Conversation

@joeldierkes
Copy link
Contributor

@joeldierkes joeldierkes commented Jan 15, 2026

Mixedbread supports organizations, a way for users to share the same stores. Let's support this in mgrep as well. Multiple users should be able to use the same store.


Note

Medium Risk
Medium risk because it changes core search/watch scoping behavior via new regex-based path filters and new config precedence, which could affect result sets and sync boundaries across users.

Overview
Adds multi-user shared mode (--shared / MGREP_SHARED / config shared: true) that changes search filtering from starts_with to regex suffix matching and adjusts sync scope so teams with different absolute paths can query the same store.

Introduces a new mgrep config command (set/get/list/reset) backed by global config file helpers in lib/config, and extends config/env/CLI precedence to include shared.

Improves org UX by caching the selected organization locally (and clearing it on logout), updates output path formatting for shared results, and expands README + bats tests to cover the new behaviors.

Written by Cursor Bugbot for commit c22e869. This will update automatically on new commits. Configure here.

joeldierkes and others added 8 commits January 30, 2026 12:42
…e changes made:

## Summary of Changes

### New Files Created

1. **`src/commands/whoami.ts`** - Shows the current authenticated user and active organization
2. **`src/commands/list-stores.ts`** - Lists all stores in the current organization

### Modified Files

1. **`src/lib/organizations.ts`** - Added organization caching functionality:
   - `cacheOrganization()` - Caches org info to `~/.mgrep/organization.json`
   - `getCachedOrganization()` - Retrieves cached org info (no network required)
   - `clearCachedOrganization()` - Clears the cache
   - `getOrganizationById()` - Gets org info by ID
   - `getCurrentOrganizationInfo()` - Gets full org info with fallback to cache
   - Updated `selectOrganization()` to cache the selected org

2. **`src/lib/store.ts`** - Added store listing capability:
   - New `StoreSummary` interface
   - New `list()` method on `Store` interface
   - Implemented in both `MixedbreadStore` and `TestStore`

3. **`src/commands/logout.ts`** - Clears cached organization on logout

4. **`src/commands/watch.ts`** - Displays organization info when starting watch

5. **`src/index.ts`** - Registered new commands (`whoami`, `listStores`)

## New CLI Commands

| Command | Description |
|---------|-------------|
| `mgrep whoami` | Shows current user and active organization |
| `mgrep list-stores` (or `mgrep ls`) | Lists all stores in the current organization |
| `mgrep switch-org` | Switch between organizations (already existed) |

## How Multi-User Support Works

1. **Organization Context**: The Mixedbread API scopes stores by organization through the JWT token. When a user logs in, they select an organization, and all subsequent API calls are scoped to that organization.

2. **Local Caching**: Organization info is cached locally at `~/.mgrep/organization.json` for display purposes without requiring network calls.

3. **Store Isolation**: Each organization has its own set of stores. Users in different organizations can have stores with the same name without conflicts.

4. **Visibility**: The `watch` command now displays the current organization and store name to help users understand which context they're working in.
1. **New `--shared` / `-S` flag** for `watch` and `search` commands
   - Enables relative path storage for multi-user collaboration

2. **Config file support** for `shared: true` in `.mgreprc.yaml`

3. **Environment variable** `MGREP_SHARED=true` support

4. **Path utility functions** in `src/lib/utils.ts`:
   - `toRelativePath()` - converts absolute to relative paths
   - `toAbsolutePath()` - converts relative to absolute paths
   - `getStoragePath()` - chooses the right path based on shared mode

5. **Updated `uploadFile()`** to accept `projectRoot` parameter and use relative paths in shared mode

6. **Updated `initialSync()`** to:
   - List store files without path prefix in shared mode
   - Compare files using storage paths (relative or absolute)
   - Delete files using storage paths

7. **Updated search command** to use relative paths for filtering in shared mode

8. **Updated `formatChunk()`** to properly display both absolute and relative paths

9. **README documentation** with:
   - Multi-User / Shared Mode section
   - Updated command tables
   - Updated config examples
   - Environment variable documentation

**Without shared mode** (default):
- Files stored as `/Users/alice/projects/myapp/src/auth.ts`
- Only works for single user

**With shared mode** (`--shared` or `shared: true`):
- Files stored as `src/auth.ts`
- Any team member can sync/search regardless of their local path
…e in README, but kept the actual command since it existed before), and `list-stores` commands:

1. **Deleted files:**
   - `src/commands/whoami.ts`
   - `src/commands/list-stores.ts`

2. **Reverted files to original:**
   - `src/lib/organizations.ts` - removed caching functions
   - `src/lib/store.ts` - removed `list()` method
   - `src/commands/logout.ts` - removed organization cache clearing

3. **Updated files:**
   - `src/index.ts` - removed imports and command registrations for `whoami` and `listStores`
   - `src/commands/watch.ts` - removed organization display code
   - `README.md` - removed `whoami` and `list-stores` from commands table and organization support section

The shared mode implementation for multi-user support remains intact.
## Changes Made

### 1. Removed unused function from `src/lib/utils.ts`
- Removed `toAbsolutePath()` function which was not used anywhere

### 2. Added tests for shared mode in `test/test.bats`
Added 6 new tests:
- **"Shared mode flag is recognized by watch"** - Tests `--shared` flag with watch command
- **"Shared mode flag is recognized by search"** - Tests `--shared` flag with search command
- **"Shared mode via config file"** - Tests `shared: true` in `.mgreprc.yaml`
- **"Shared mode via environment variable"** - Tests `MGREP_SHARED=true` env var
- **"Shared mode stores files with relative paths"** - Verifies paths don't contain absolute directory
- **"Shared mode search with subdirectory"** - Tests subdirectory path filtering in shared mode
## Bug Fixes

### 1. Double slash in file paths (Medium Severity)
**Problem**: When `storedPath` was `/Users/alice/project/src/file.ts` and `pwd` was `/Users/alice/project`, the code produced `.//src/file.ts` because:
- `storedPath.replace(pwd, "")` → `/src/file.ts` (leading slash remains)
- `./${displayPath}` → `.//src/file.ts`

**Fix**: Added `.replace(/^\//, "")` to strip the leading slash after removing the pwd prefix.

### 2. Sync path root inconsistent with filter path in shared mode (Medium Severity)
**Problem**: When running `mgrep --shared --sync "query" sub`:
- Files were synced from `sub/` directory, stored as `file.ts` (relative to `sub/`)
- But filter used `toRelativePath(search_path, root)` = `sub`
- Filter `path starts_with "sub"` wouldn't match `file.ts`

**Fix**: In shared mode, sync always uses the project root (`root`) instead of `search_path`. This ensures files are stored as `sub/file.ts` (relative to project root), so the filter `sub` correctly matches.
1. **Windows absolute path detection** - Changed `storedPath.startsWith("/")` to `isAbsolute(storedPath)` which properly handles both Unix (`/path`) and Windows (`C:\path`) absolute paths. Also fixed `exec_path?.startsWith("/")` to use `isAbsolute()`.

2. **Home directory safety check bypass** - Moved the `syncRoot` calculation before the home directory check so that in shared mode (where `syncRoot = root`), the check validates the actual directory being synced, not just the search path argument.

Summary of changes in `src/commands/search.ts`:
- Line 1: Added `isAbsolute` import
- Lines 100-112: Changed path detection in `formatChunk` to use `isAbsolute()` and updated regex to handle both Unix and Windows path separators (`/^[\\/]/`)
- Lines 300-306: Moved `syncRoot` calculation before the home directory check
- Lines 307-316: Now validates `syncRoot` instead of `search_path` for the home directory safety check
**New command: `mgrep config`** with four subcommands:

- `mgrep config set <key> <value>` — Set a global config value
- `mgrep config get <key>` — Get a global config value (shows `(default)` if unset)
- `mgrep config list` — List all config values
- `mgrep config reset [key]` — Reset one key or all to defaults

**Supported keys:** `maxFileSize`, `maxFileCount`, `shared`

**Files changed:**
- `src/commands/config.ts` (new) — The config command with type-safe value parsing and validation
- `src/lib/config.ts` — Exported `DEFAULT_CONFIG`, `GLOBAL_CONFIG_DIR`, `getGlobalConfigPaths`, `CONFIG_KEYS`, and added `readGlobalConfig`, `writeGlobalConfig`, `getGlobalConfigFilePath` helpers
- `src/index.ts` — Registered the config command
- `test/test.bats` — Added 7 tests covering set/get, list, reset, and error cases
- Fix config reset single key bug where writeGlobalConfig re-merged
  the deleted key from file. Added saveGlobalConfig for direct writes.
- Add fs.existsSync check before unlinkSync on reset single key
- Remove unused Store.list() method and StoreSummary interface

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@joeldierkes joeldierkes force-pushed the vk/f5b2-add-multi-user-s branch from 769d536 to a39945d Compare January 30, 2026 20:45
joeldierkes and others added 2 commits January 30, 2026 18:04
…nById

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- **Build**: Clean
- **Lint**: No issues (31 files checked)
- **Tests**: 51/51 passing

The shared mode simplification is complete. The `SearchFilter` type cast was needed because the SDK types don't include the `"regex"` operator, but the API supports it. The cast at the call sites (`filters as SearchFilter | undefined`) is the cleanest approach to handle this SDK gap.
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

} catch {
return null;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported function getCachedOrganization is never used

Low Severity

The getCachedOrganization function is exported but never imported or called anywhere in the codebase. While cacheOrganization (write) and clearCachedOrganization (delete) are both used, this "read" function is dead code that adds unnecessary maintenance burden.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant