@unifiedmemory/cli 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -1,29 +1,44 @@
1
1
  # UnifiedMemory CLI Configuration
2
- # Copy this file to .env and fill in your values
2
+ #
3
+ # ============================================
4
+ # IMPORTANT: This file is OPTIONAL
5
+ # ============================================
6
+ # The CLI includes production defaults for all values below.
7
+ # This file is only needed if you want to override defaults
8
+ # (e.g., for development, testing, or custom deployments).
9
+ #
10
+ # For normal usage, you can delete this file - the CLI will work without it.
11
+ #
12
+ # To use custom values:
13
+ # 1. Copy this file to .env
14
+ # 2. Uncomment and modify the values you want to override
15
+ # 3. Leave other values commented to use the built-in defaults
3
16
 
4
17
  # ============================================
5
- # REQUIRED: Clerk OAuth Configuration
18
+ # Clerk OAuth Configuration
6
19
  # ============================================
7
- # Get these from your Clerk dashboard: https://dashboard.clerk.com
8
- CLERK_CLIENT_ID=your_clerk_client_id_here
9
- CLERK_DOMAIN=your-app.clerk.accounts.dev
20
+ # Production defaults are included in the CLI
21
+ # Only override these if using a custom Clerk instance
22
+ # CLERK_CLIENT_ID=custom_clerk_client_id
23
+ # CLERK_DOMAIN=custom-app.clerk.accounts.dev
10
24
 
11
25
  # ============================================
12
- # REQUIRED: API Configuration
26
+ # API Configuration
13
27
  # ============================================
14
- # Your UnifiedMemory API gateway URL
15
- API_ENDPOINT=https://your-api-gateway.zuplo.dev
28
+ # Production default is included in the CLI
29
+ # Only override this for local testing or custom deployments
30
+ # API_ENDPOINT=https://custom-api-gateway.zuplo.dev
16
31
 
17
32
  # ============================================
18
- # OPTIONAL: OAuth Flow Configuration
33
+ # OAuth Flow Configuration
19
34
  # ============================================
20
- # Customize the OAuth redirect URI and local server port
21
- # Default values work for most users
35
+ # Defaults to localhost:3333 for the OAuth callback server
36
+ # Customize only if you need a different port or URL
22
37
  # REDIRECT_URI=http://localhost:3333/callback
23
38
  # PORT=3333
24
39
 
25
40
  # ============================================
26
- # OPTIONAL: Clerk Client Secret
41
+ # Clerk Client Secret (Optional)
27
42
  # ============================================
28
43
  # Not required for PKCE flow (recommended)
29
44
  # Only set this if specifically instructed
package/CHANGELOG.md CHANGED
@@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+
12
+ - **lib/config.js** - Embedded production defaults for zero-configuration installation
13
+ - Added default values for `CLERK_CLIENT_ID`, `CLERK_DOMAIN`, and `API_ENDPOINT`
14
+ - Users no longer need to create `.env` files after `npm install`
15
+ - Environment variables can still override defaults for development/testing
16
+ - Simplified `validateConfig()` to remove "missing environment variables" errors
17
+ - CLI now works immediately after installation with no setup required
18
+
19
+ - **.env.example** - Updated documentation to clarify optional nature
20
+ - Added prominent note that the file is OPTIONAL
21
+ - Explained that production defaults are included in the CLI
22
+ - Commented out all example values to emphasize optional overrides
23
+ - Clarified when overrides might be needed (development, testing, custom deployments)
24
+
25
+ - **README.md** - Improved installation and configuration documentation
26
+ - Added "No configuration required!" note to installation section
27
+ - Renamed "Configuration" to "Advanced Configuration (Optional)"
28
+ - Removed "Missing environment variables" from troubleshooting
29
+ - Emphasized zero-friction installation experience
30
+
31
+ ## [1.2.0] - 2026-01-08
32
+
33
+ ### Added
34
+
35
+ - **commands/init.js** - Smart default project name based on directory detection
36
+ - Project name prompt now defaults to the parent top-level directory name
37
+ - Automatically detects project root by walking up directories looking for markers:
38
+ - `.git` directory (Git repository)
39
+ - `package.json` (Node.js project)
40
+ - `pyproject.toml` (Python project)
41
+ - `Cargo.toml` (Rust project)
42
+ - `.um` directory (already initialized)
43
+ - Works correctly from nested subdirectories (e.g., `/project/src/components/`)
44
+ - Max depth limit of 5 levels prevents walking too far up (e.g., to home directory)
45
+ - Users can press Enter to accept default or type custom name
46
+ - Graceful fallback to current directory name if no markers found within 5 levels
47
+ - New helper functions: `findProjectRoot()` and `getDefaultProjectName()`
48
+ - No new dependencies - uses built-in Node.js `path` and `fs` modules
49
+
50
+ - **commands/init.js, lib/provider-detector.js** - Auto-authorize MCP tools in Claude Code during init
51
+ - Dynamically fetches available MCP tools from the gateway during `um init`
52
+ - Prompts user once to pre-authorize all UnifiedMemory tools (search_notes, create_note, create_topic, etc.)
53
+ - If user agrees, adds tool permissions to `.claude/settings.local.json` in format `mcp__unifiedmemory__<toolname>`
54
+ - Future-proof: Automatically includes new tools as they're added to the MCP server
55
+ - Single yes/no question with clear explanation of what's being authorized
56
+ - Graceful error handling: Skips permission setup if API unavailable, continues with init
57
+ - Idempotent: Won't duplicate existing permissions if run multiple times
58
+ - Preserves existing permissions (e.g., Bash commands) when adding new ones
59
+ - New helper function: `fetchMCPToolPermissions()` in commands/init.js
60
+ - Updated `ClaudeProvider.updateClaudeSettings()` to write permissions array
61
+ - Eliminates need for manual "Allow" clicks on every MCP tool use
62
+
10
63
  ### Fixed
11
64
 
12
65
  - **commands/init.js** - Enhanced error handling for 401/403/network errors during project fetching
package/README.md CHANGED
@@ -171,6 +171,8 @@ npm install -g @unifiedmemory/cli
171
171
  um --version
172
172
  ```
173
173
 
174
+ **No configuration required!** The CLI includes production defaults and works immediately after installation.
175
+
174
176
  **Option 2: Use with npx (no installation)**
175
177
  ```bash
176
178
  npx @unifiedmemory/cli init
@@ -213,43 +215,35 @@ Tokens automatically refresh. If you see auth errors:
213
215
  um login
214
216
  ```
215
217
 
216
- ### Missing environment variables
217
- If you see "Missing required environment variables" error:
218
- 1. Copy `.env.example` to `.env` in the CLI installation directory
219
- 2. Fill in your Clerk and API credentials
220
- 3. See Configuration section below for details
218
+ ## Advanced Configuration (Optional)
221
219
 
222
- ## Configuration
220
+ **For most users, no configuration is needed!** The CLI includes production defaults and works out of the box.
223
221
 
224
- The CLI requires environment variables for Clerk OAuth and API access. These should never be hardcoded in your project.
222
+ For development, testing, or custom deployments, you can override defaults by creating a `.env` file:
225
223
 
226
- **Step 1: Create `.env` file**
224
+ **Step 1: Create `.env` file** (optional)
227
225
  ```bash
228
226
  # In the um-cli installation directory (find with: npm root -g)
229
227
  cp .env.example .env
230
228
  ```
231
229
 
232
- **Step 2: Add your credentials**
230
+ **Step 2: Override the values you need** (optional)
233
231
  ```bash
234
- # Required: Clerk OAuth Configuration
235
- CLERK_CLIENT_ID=your_clerk_client_id_here
236
- CLERK_DOMAIN=your-app.clerk.accounts.dev
237
-
238
- # Required: API Configuration
239
- API_ENDPOINT=https://your-api-gateway.zuplo.dev
240
- ```
232
+ # Optional: Override production defaults
233
+ CLERK_CLIENT_ID=custom_clerk_client_id
234
+ CLERK_DOMAIN=custom-app.clerk.accounts.dev
235
+ API_ENDPOINT=https://custom-api-gateway.zuplo.dev
241
236
 
242
- Get these values from:
243
- - **Clerk credentials**: [Clerk Dashboard](https://dashboard.clerk.com)
244
- - **API endpoint**: Your UnifiedMemory deployment
245
-
246
- **Optional configuration**:
247
- ```bash
248
- # Customize OAuth redirect (defaults shown)
237
+ # Optional: Customize OAuth redirect (defaults to localhost:3333)
249
238
  REDIRECT_URI=http://localhost:3333/callback
250
239
  PORT=3333
251
240
  ```
252
241
 
242
+ **When you might need this**:
243
+ - 🔧 **Development**: Testing with a local API backend
244
+ - 🧪 **Testing**: Using a staging or test Clerk instance
245
+ - 🏢 **Custom Deployment**: Running your own UnifiedMemory instance
246
+
253
247
  See `.env.example` for the complete template with documentation.
254
248
 
255
249
  ## Privacy & Security
package/commands/init.js CHANGED
@@ -8,6 +8,52 @@ import { loadAndRefreshToken } from '../lib/token-validation.js';
8
8
  import { login } from './login.js';
9
9
  import { ProviderDetector } from '../lib/provider-detector.js';
10
10
 
11
+ /**
12
+ * Find project root directory by looking for common markers
13
+ * @param {string} startDir - Directory to start searching from
14
+ * @returns {string} Project root directory path
15
+ */
16
+ function findProjectRoot(startDir = process.cwd()) {
17
+ let currentDir = startDir;
18
+ const root = path.parse(currentDir).root;
19
+ let levelsUp = 0;
20
+ const MAX_DEPTH = 5; // Only walk up 5 levels to avoid finding distant markers
21
+
22
+ while (currentDir !== root && levelsUp < MAX_DEPTH) {
23
+ // Check for common project markers
24
+ const markers = [
25
+ path.join(currentDir, '.git'),
26
+ path.join(currentDir, 'package.json'),
27
+ path.join(currentDir, 'pyproject.toml'),
28
+ path.join(currentDir, 'Cargo.toml'),
29
+ path.join(currentDir, '.um'),
30
+ ];
31
+
32
+ // If any marker exists, this is the project root
33
+ for (const marker of markers) {
34
+ if (fs.existsSync(marker)) {
35
+ return currentDir;
36
+ }
37
+ }
38
+
39
+ // Move up one directory
40
+ currentDir = path.dirname(currentDir);
41
+ levelsUp++;
42
+ }
43
+
44
+ // Fallback: return the starting directory
45
+ return startDir;
46
+ }
47
+
48
+ /**
49
+ * Get default project name based on directory
50
+ * @returns {string} Default project name
51
+ */
52
+ function getDefaultProjectName() {
53
+ const projectRoot = findProjectRoot();
54
+ return path.basename(projectRoot);
55
+ }
56
+
11
57
  export async function init(options = {}) {
12
58
  console.log(chalk.cyan('\n🚀 UnifiedMemory Initialization\n'));
13
59
 
@@ -35,9 +81,15 @@ export async function init(options = {}) {
35
81
  // Step 3: Save project config
36
82
  await saveProjectConfig(authData, projectData);
37
83
 
84
+ // Step 3.5: Fetch available MCP tools for permissions
85
+ let mcpToolPermissions = null;
86
+ if (!options.skipConfigure) {
87
+ mcpToolPermissions = await fetchMCPToolPermissions(authData, projectData);
88
+ }
89
+
38
90
  // Step 4: Configure AI tools
39
91
  if (!options.skipConfigure) {
40
- await configureProviders(authData, projectData);
92
+ await configureProviders(authData, projectData, mcpToolPermissions);
41
93
  }
42
94
 
43
95
  console.log(chalk.green('\n✅ Initialization complete!\n'));
@@ -228,11 +280,14 @@ async function selectOrCreateProject(authData, options) {
228
280
  }]);
229
281
 
230
282
  if (action === 'create') {
283
+ const defaultName = getDefaultProjectName();
284
+
231
285
  const { name, description } = await inquirer.prompt([
232
286
  {
233
287
  type: 'input',
234
288
  name: 'name',
235
289
  message: 'Project name:',
290
+ default: defaultName,
236
291
  validate: input => input.length > 0 || 'Name is required',
237
292
  },
238
293
  {
@@ -246,11 +301,14 @@ async function selectOrCreateProject(authData, options) {
246
301
  } else {
247
302
  if (projects.length === 0) {
248
303
  console.log(chalk.yellow('No projects found. Creating first project...'));
304
+ const defaultName = getDefaultProjectName();
305
+
249
306
  const { name, description } = await inquirer.prompt([
250
307
  {
251
308
  type: 'input',
252
309
  name: 'name',
253
310
  message: 'Project name:',
311
+ default: defaultName,
254
312
  validate: input => input.length > 0 || 'Name is required',
255
313
  },
256
314
  {
@@ -440,7 +498,66 @@ async function saveProjectConfig(authData, projectData) {
440
498
  }
441
499
  }
442
500
 
443
- async function configureProviders(authData, projectData) {
501
+ /**
502
+ * Fetch MCP tools and prompt user for permission to pre-authorize
503
+ * @param {Object} authData - Auth data with tokens
504
+ * @param {Object} projectData - Project data
505
+ * @returns {Promise<Array<string>|null>} - Array of permission strings or null if declined
506
+ */
507
+ async function fetchMCPToolPermissions(authData, projectData) {
508
+ try {
509
+ // Build auth headers (similar to lib/mcp-server.js)
510
+ const authHeaders = {
511
+ 'Authorization': `Bearer ${authData.access_token}`,
512
+ 'X-Org-Id': authData.org_id,
513
+ 'X-User-Id': authData.user_id,
514
+ };
515
+
516
+ // Fetch tools from gateway
517
+ const { fetchRemoteMCPTools } = await import('../lib/mcp-proxy.js');
518
+ const projectContext = {
519
+ project_id: projectData.project_id,
520
+ org_id: authData.org_id,
521
+ };
522
+
523
+ const toolsResult = await fetchRemoteMCPTools(authHeaders, projectContext);
524
+ const tools = toolsResult.tools || [];
525
+
526
+ if (tools.length === 0) {
527
+ return null; // No tools available
528
+ }
529
+
530
+ // Format tool names for display
531
+ const toolNames = tools.map(t => t.name).join(', ');
532
+
533
+ // Prompt user
534
+ console.log(chalk.cyan('\n🔧 MCP Tool Permissions\n'));
535
+ console.log(chalk.gray(`Found ${tools.length} available tools: ${toolNames}`));
536
+
537
+ const { allowPermissions } = await inquirer.prompt([{
538
+ type: 'confirm',
539
+ name: 'allowPermissions',
540
+ message: 'Pre-authorize these UnifiedMemory tools in Claude Code? (Recommended)',
541
+ default: true,
542
+ }]);
543
+
544
+ if (!allowPermissions) {
545
+ console.log(chalk.yellow('⚠ Tools not pre-authorized. Claude will prompt for permission on first use.'));
546
+ return null;
547
+ }
548
+
549
+ // Convert tool names to permission format
550
+ const permissions = tools.map(tool => `mcp__unifiedmemory__${tool.name}`);
551
+ return permissions;
552
+
553
+ } catch (error) {
554
+ console.error(chalk.yellow(`⚠ Could not fetch MCP tools: ${error.message}`));
555
+ console.log(chalk.gray('Skipping permission setup. You can manually add permissions later.'));
556
+ return null;
557
+ }
558
+ }
559
+
560
+ async function configureProviders(authData, projectData, mcpToolPermissions = null) {
444
561
  console.log(chalk.cyan('\n🔧 Configuring AI code assistants...\n'));
445
562
 
446
563
  // Pass current directory for project-level configs (like Claude Code)
@@ -456,8 +573,8 @@ async function configureProviders(authData, projectData) {
456
573
 
457
574
  // NEW APPROACH: Configure local MCP server (no tokens in config files)
458
575
  for (const provider of detected) {
459
- // Configure MCP server
460
- const mcpSuccess = provider.configureMCP();
576
+ // Configure MCP server (pass permissions for Claude Code)
577
+ const mcpSuccess = provider.configureMCP(mcpToolPermissions);
461
578
 
462
579
  // Configure memory instructions
463
580
  const instructionsResult = provider.configureMemoryInstructions?.();
@@ -465,6 +582,11 @@ async function configureProviders(authData, projectData) {
465
582
  // Display results
466
583
  if (mcpSuccess) {
467
584
  console.log(chalk.green(`✓ Configured ${provider.name} MCP server`));
585
+
586
+ // Show permission status for Claude Code
587
+ if (provider.name === 'Claude Code' && mcpToolPermissions && mcpToolPermissions.length > 0) {
588
+ console.log(chalk.green(` ✓ Pre-authorized ${mcpToolPermissions.length} MCP tools`));
589
+ }
468
590
  } else {
469
591
  console.log(chalk.red(`✗ Failed to configure ${provider.name} MCP`));
470
592
  }
package/lib/config.js CHANGED
@@ -9,49 +9,37 @@ const __dirname = dirname(__filename);
9
9
  dotenvConfig({ path: join(__dirname, '..', '.env') });
10
10
 
11
11
  export const config = {
12
- // Required: Clerk OAuth configuration
13
- clerkClientId: process.env.CLERK_CLIENT_ID,
12
+ // Clerk OAuth configuration (production defaults, can be overridden via env vars)
13
+ clerkClientId: process.env.CLERK_CLIENT_ID || 'nULlnomaKB9rRGP2',
14
14
  clerkClientSecret: process.env.CLERK_CLIENT_SECRET, // Optional for PKCE flow
15
- clerkDomain: process.env.CLERK_DOMAIN,
15
+ clerkDomain: process.env.CLERK_DOMAIN || 'clear-caiman-45.clerk.accounts.dev',
16
16
 
17
- // Required: API configuration
18
- apiEndpoint: process.env.API_ENDPOINT,
17
+ // API configuration (production default, can be overridden via env var)
18
+ apiEndpoint: process.env.API_ENDPOINT || 'https://rose-asp-main-1c0b114.d2.zuplo.dev',
19
19
 
20
- // Optional: OAuth flow configuration (non-sensitive defaults OK)
20
+ // OAuth flow configuration (localhost defaults for callback server)
21
21
  redirectUri: process.env.REDIRECT_URI || 'http://localhost:3333/callback',
22
22
  port: parseInt(process.env.PORT || '3333', 10)
23
23
  };
24
24
 
25
- // Validation function - ensures required configuration is present
25
+ // Validation function - validates configuration values
26
26
  export function validateConfig() {
27
- const missing = [];
28
-
29
- if (!config.clerkClientId) {
30
- missing.push('CLERK_CLIENT_ID');
31
- }
32
-
33
- if (!config.clerkDomain) {
34
- missing.push('CLERK_DOMAIN');
35
- }
36
-
37
- if (!config.apiEndpoint) {
38
- missing.push('API_ENDPOINT');
39
- }
40
-
41
- if (missing.length > 0) {
42
- throw new Error(
43
- `Missing required environment variables: ${missing.join(', ')}\n\n` +
44
- `Please create a .env file in the project root with these values.\n` +
45
- `See .env.example for a template.`
46
- );
47
- }
48
-
49
- // Validate URL format for apiEndpoint
27
+ // Validate URL format for apiEndpoint (now guaranteed to exist via defaults)
50
28
  try {
51
29
  new URL(config.apiEndpoint);
52
30
  } catch (e) {
53
31
  throw new Error(`API_ENDPOINT must be a valid URL (got: ${config.apiEndpoint})`);
54
32
  }
55
33
 
34
+ // Validate clerkDomain is not empty (basic sanity check)
35
+ if (!config.clerkDomain || config.clerkDomain.trim() === '') {
36
+ throw new Error('CLERK_DOMAIN cannot be empty');
37
+ }
38
+
39
+ // Validate clerkClientId is not empty (basic sanity check)
40
+ if (!config.clerkClientId || config.clerkClientId.trim() === '') {
41
+ throw new Error('CLERK_CLIENT_ID cannot be empty');
42
+ }
43
+
56
44
  return true;
57
45
  }
@@ -59,9 +59,10 @@ class BaseProvider {
59
59
  }
60
60
  }
61
61
 
62
- configureMCP() {
62
+ configureMCP(toolPermissions = null) {
63
63
  // NEW APPROACH: Configure local server instead of HTTP
64
64
  // No parameters needed - local server reads from filesystem
65
+ // toolPermissions parameter for Claude Code compatibility (unused in base class)
65
66
  const config = this.readConfig() || { mcpServers: {} };
66
67
 
67
68
  config.mcpServers = config.mcpServers || {};
@@ -136,19 +137,19 @@ class ClaudeProvider extends BaseProvider {
136
137
  }
137
138
  }
138
139
 
139
- configureMCP() {
140
+ configureMCP(toolPermissions = null) {
140
141
  // Create .mcp.json at project root with local server config
141
142
  const success = super.configureMCP();
142
143
 
143
144
  if (success) {
144
145
  // Update .claude/settings.local.json to enable the MCP server
145
- this.updateClaudeSettings();
146
+ this.updateClaudeSettings(toolPermissions);
146
147
  }
147
148
 
148
149
  return success;
149
150
  }
150
151
 
151
- updateClaudeSettings() {
152
+ updateClaudeSettings(toolPermissions = null) {
152
153
  if (!this.settingsPath) return false;
153
154
 
154
155
  try {
@@ -169,6 +170,23 @@ class ClaudeProvider extends BaseProvider {
169
170
  settings.enabledMcpjsonServers.push('unifiedmemory');
170
171
  }
171
172
 
173
+ // Add tool permissions if provided
174
+ if (toolPermissions && Array.isArray(toolPermissions) && toolPermissions.length > 0) {
175
+ if (!settings.permissions) {
176
+ settings.permissions = { allow: [] };
177
+ }
178
+ if (!settings.permissions.allow) {
179
+ settings.permissions.allow = [];
180
+ }
181
+
182
+ // Add new permissions, avoiding duplicates
183
+ toolPermissions.forEach(permission => {
184
+ if (!settings.permissions.allow.includes(permission)) {
185
+ settings.permissions.allow.push(permission);
186
+ }
187
+ });
188
+ }
189
+
172
190
  // Write settings
173
191
  fs.ensureDirSync(this.claudeDir);
174
192
  fs.writeJSONSync(this.settingsPath, settings, { spaces: 2 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifiedmemory/cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "UnifiedMemory CLI - AI code assistant integration",
5
5
  "main": "index.js",
6
6
  "type": "module",