@lanonasis/cli 3.8.1 โ†’ 3.9.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,224 @@
1
+ # Changelog - @lanonasis/cli
2
+
3
+ ## [3.9.1] - 2026-02-01
4
+
5
+ ### ๐Ÿ” Authentication Fixes
6
+
7
+ - **OAuth Scope Clarification**: OAuth login now clearly states it enables MCP integration only
8
+ - **Improved Error Messages**: 401 errors for OAuth users include specific guidance for direct API access
9
+ - **Removed Misleading Storage**: OAuth tokens are no longer incorrectly stored as vendor keys
10
+ - **Documentation Updates**: README and in-CLI help clarify authentication method differences
11
+
12
+ ### ๐Ÿ› Bug Fixes
13
+
14
+ - Fixed confusing error message when OAuth users try to use direct CLI commands
15
+ - Removed `.lanonasis/mcp-config.json` from version control
16
+
17
+ ## [3.9.0] - 2026-02-01
18
+
19
+ ### ๐ŸŽจ CLI UX Revolution
20
+
21
+ #### Seamless Multi-Line Text Input
22
+ - **Inline Text Editor**: Professional multi-line text input without external editors
23
+ - Raw terminal mode for keystroke capture
24
+ - Full editing support (arrow keys, backspace, newlines)
25
+ - Visual feedback with line numbers and cursor indicators
26
+ - Submit with Ctrl+D, cancel with Ctrl+C
27
+ - Configurable fallback to external editors
28
+
29
+ #### Intelligent MCP Server Connection Management
30
+ - **Auto-Configuration**: Automatically detects and configures embedded MCP servers
31
+ - **Connection Lifecycle**: Smart server process management with health monitoring
32
+ - **Persistent Configuration**: Saves and loads user preferences across sessions
33
+ - **Connection Verification**: Validates server connectivity before operations
34
+ - **Graceful Error Handling**: Clear error messages with actionable resolution steps
35
+
36
+ #### First-Run Onboarding Experience
37
+ - **Guided Setup**: Interactive onboarding flow for new users
38
+ - **Connectivity Testing**: Automatic testing of API endpoints and services
39
+ - **Smart Defaults**: Configures optimal settings based on environment
40
+ - **User Preferences**: Captures and persists input mode, editor choice, and behavior preferences
41
+ - **Troubleshooting Guidance**: Context-aware help when issues are detected
42
+
43
+ ### ๐Ÿ” Authentication Clarifications
44
+
45
+ #### OAuth vs Direct API Access
46
+ - **Clear Scope Documentation**: OAuth2 login now explicitly states it enables MCP integration
47
+ - **Improved Error Messages**: 401 errors for OAuth users include specific guidance for direct API access
48
+ - **Authentication Method Guidance**: CLI provides clear instructions for:
49
+ - **OAuth**: Use for MCP integration and real-time features
50
+ - **Vendor Key**: Obtain from dashboard for direct API access (`lanonasis auth login --vendor`)
51
+ - **Credentials**: Use username/password for direct API access (`lanonasis auth login --credentials`)
52
+
53
+ #### Secure Storage Fallback
54
+ - **Keytar Optional**: When keytar (native secure storage) is unavailable, CLI gracefully falls back to encrypted file storage
55
+ - **Cross-Platform**: Encrypted storage works consistently across all platforms
56
+ - **No Data Loss**: Credentials are preserved in `~/.lanonasis/api-key.enc` with AES-256-GCM encryption
57
+
58
+ ### ๐Ÿ› Critical Bug Fixes (PR #93)
59
+
60
+ #### P1: Connection Verification False Positive
61
+ - **Issue**: `verifyConnection()` returned `true` even when server was in error/stopped state
62
+ - **Fix**: Added explicit checks for error and stopped states before declaring success
63
+ - **Impact**: Users will now see accurate connection status instead of false positives
64
+
65
+ #### P2: Configuration Not Loaded Before Use
66
+ - **Issue**: `ConnectionManager.init()` method existed but was never called
67
+ - **Fix**: Added `init()` to ConnectionManager interface and call it before `connectLocal()`
68
+ - **Impact**: User configuration is now properly loaded and respected
69
+
70
+ #### P2: Empty Content Overwrites in Inline Updates
71
+ - **Issue**: When updating memories in inline mode, `defaultContent` wasn't passed to TextInputHandler
72
+ - **Fix**: Added `defaultContent` support throughout the text input pipeline
73
+ - **Impact**: Memory updates preserve existing content instead of starting with blank slate
74
+
75
+ ### ๐Ÿงช Testing & Quality
76
+
77
+ - **Comprehensive Test Suite**: 168 passing tests including property-based tests
78
+ - **Zero TypeScript Errors**: All compilation errors resolved
79
+ - **No Regressions**: All existing tests continue to pass
80
+ - **Professional Documentation**: Complete inline documentation and type definitions
81
+
82
+ ### ๐Ÿ“ฆ Package Cleanup
83
+
84
+ - **npmignore**: Excludes test files and development artifacts from published package
85
+ - **Directory Reorganization**: Cleaner structure with examples moved to `docs/examples/`
86
+ - **Build Optimization**: Reduced package size by excluding unnecessary files
87
+
88
+ ### ๐Ÿ”„ Breaking Changes
89
+ None - Fully backward compatible
90
+
91
+ ### ๐Ÿ“ Technical Details
92
+
93
+ **New Implementations**:
94
+ - `TextInputHandlerImpl` ([cli/src/ux/implementations/TextInputHandlerImpl.ts](cli/src/ux/implementations/TextInputHandlerImpl.ts))
95
+ - `ConnectionManagerImpl` ([cli/src/ux/implementations/ConnectionManagerImpl.ts](cli/src/ux/implementations/ConnectionManagerImpl.ts))
96
+ - `OnboardingFlowImpl` ([cli/src/ux/implementations/OnboardingFlowImpl.ts](cli/src/ux/implementations/OnboardingFlowImpl.ts))
97
+
98
+ **Integration Points**:
99
+ - Memory commands now use inline text input by default ([cli/src/commands/memory.ts](cli/src/commands/memory.ts:116-119))
100
+ - MCP connect command uses ConnectionManager ([cli/src/commands/mcp.ts](cli/src/commands/mcp.ts:130-137))
101
+ - Init command includes onboarding flow ([cli/src/commands/init.ts](cli/src/commands/init.ts))
102
+
103
+ ## [3.7.0] - 2025-11-23
104
+
105
+ ### ๐Ÿ” Security Infrastructure Upgrade
106
+
107
+ #### Enhanced API Key Security with SHA-256
108
+ - **Cross-Platform SHA-256 Hashing**: Unified hash utilities for consistent API key hashing across all platforms
109
+ - **Local Hash Implementation**: Isolated hash utilities (`src/utils/hash-utils.ts`) for CLI independence
110
+ - **Double-Hash Prevention**: Smart detection of pre-hashed keys to prevent double-hashing errors
111
+ - **Server-Side Validation**: Constant-time comparison for timing-attack prevention
112
+ - **Future NPM Package Ready**: Designed for eventual migration to `@lanonasis/security` npm package
113
+
114
+ #### Technical Improvements
115
+ - **Build Stability**: Fixed TypeScript `rootDir` compilation errors
116
+ - **Zero Deprecation Warnings**: All dependencies verified for production readiness
117
+ - **Cross-Platform Compatibility**: Node.js crypto for server-side, Web Crypto API fallback for browser contexts
118
+ - **Type Safety**: Full TypeScript support with exported hash types (`ApiKeyHash`, `ApiKey`)
119
+
120
+ #### Hash Utility Functions
121
+ ```typescript
122
+ // Available in CLI
123
+ ensureApiKeyHash(apiKey: string): string // Smart hash normalization
124
+ hashApiKey(apiKey: string): string // SHA-256 hashing
125
+ isSha256Hash(value: string): boolean // Hash detection
126
+ ```
127
+
128
+ ### ๐Ÿ›ก๏ธ Security Features
129
+ - โœ… SHA-256 cryptographic hashing for all API keys
130
+ - โœ… Prevents plaintext key transmission
131
+ - โœ… Constant-time hash comparison
132
+ - โœ… Automatic hash detection and normalization
133
+ - โœ… Compatible with existing vendor key authentication
134
+
135
+ ### ๐Ÿ”„ Breaking Changes
136
+ None - Fully backward compatible
137
+
138
+ ### ๐Ÿ“ฆ Dependencies
139
+ - No new external dependencies
140
+ - Uses native Node.js `crypto` module
141
+ - Clean build with zero deprecation warnings
142
+
143
+ ## [3.0.1] - 2025-10-08
144
+
145
+ ### ๐Ÿš€ Major Version Bump
146
+ This is a major version release (3.0) due to the significant MCP architectural changes and new capabilities that may affect existing integrations.
147
+
148
+ ### ๐ŸŽ‰ Major Features
149
+ *Same as 2.0.9 but republished as 3.0.1 due to npm version conflict*
150
+
151
+ ## [2.0.9] - 2025-10-08 (npm publish conflict)
152
+
153
+ ### ๐ŸŽ‰ Major Features
154
+
155
+ #### Enhanced Model Context Protocol (MCP) Support
156
+ - **Multi-Server Connections**: Connect to multiple MCP servers simultaneously with automatic failover
157
+ - **Advanced Error Handling**: Exponential backoff retry logic and graceful degradation
158
+ - **Health Monitoring**: Automatic health checks with latency tracking and auto-reconnection
159
+ - **Connection Pooling**: Efficient resource management for multiple connections
160
+ - **Tool Chain Execution**: Support for sequential and parallel tool execution
161
+
162
+ #### New MCP Infrastructure
163
+ - **Enhanced MCP Client** (`src/mcp/client/enhanced-client.ts`)
164
+ - Multi-server management with priority-based selection
165
+ - Event-driven architecture with connection status tracking
166
+ - Automatic failover to backup servers
167
+
168
+ - **MCP Server Implementation** (`src/mcp/server/lanonasis-server.ts`)
169
+ - Full MCP protocol compliance (tools, resources, prompts)
170
+ - 16 registered tools for memory, topic, and system operations
171
+ - Resource providers for data access
172
+ - Interactive prompts for user guidance
173
+
174
+ - **Transport Support** (`src/mcp/transports/transport-manager.ts`)
175
+ - StdIO transport for local processes
176
+ - WebSocket transport with auto-reconnection
177
+ - SSE (Server-Sent Events) for streaming
178
+ - Authentication support (Bearer, API Key, Basic)
179
+
180
+ - **Schema Validation** (`src/mcp/schemas/tool-schemas.ts`)
181
+ - Zod-based validation for all MCP tools
182
+ - Type-safe operations with clear error messages
183
+ - Comprehensive schemas for memory, topic, API key, and system operations
184
+
185
+ ### ๐Ÿ›  Technical Improvements
186
+ - **Build System**: New MCP-specific build scripts (`build:mcp`, `dev:mcp`, `test:mcp`)
187
+ - **Module Structure**: Dedicated `/src/mcp/` directory with clean separation of concerns
188
+ - **Error Recovery**: Improved error handling throughout the MCP stack
189
+ - **Type Safety**: Full TypeScript support with proper type definitions
190
+
191
+ ### ๐Ÿ› Bug Fixes
192
+ - Fixed authentication flow issues with proper token validation
193
+ - Resolved double slash URL construction in CLI auth
194
+ - Fixed memory command authentication requirements
195
+ - Corrected error messages to show correct authentication command
196
+
197
+ ### ๐Ÿ“š Documentation
198
+ - Enhanced README with MCP usage examples
199
+ - Added MCP Server Mode documentation
200
+ - Updated command reference with new MCP features
201
+ - Created comprehensive MCP enhancement summary
202
+
203
+ ### โš ๏ธ Breaking Changes
204
+ - HTTP transport temporarily disabled in favor of WebSocket/StdIO (more reliable)
205
+ - Some API key operations pending full implementation
206
+
207
+ ### ๐Ÿ”„ Dependencies
208
+ - Updated to latest @modelcontextprotocol/sdk
209
+ - Added ws for WebSocket support
210
+ - Added zod for schema validation
211
+
212
+ ## [2.0.8] - Previous Version
213
+ - Authentication system improvements
214
+ - CLI guided setup enhancements
215
+ - Performance optimizations
216
+
217
+ ## [2.0.7] - Previous Version
218
+ - Memory management improvements
219
+ - Topic organization features
220
+ - Bug fixes and stability improvements
221
+
222
+ ---
223
+
224
+ For full release notes and migration guides, visit: https://docs.lanonasis.com/cli/changelog
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # @lanonasis/cli v3.7.0 - Enterprise Security & MCP Experience
1
+ # @lanonasis/cli v3.9.0 - Enterprise Security & Professional UX
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/@lanonasis/cli)](https://www.npmjs.com/package/@lanonasis/cli)
4
4
  [![Downloads](https://img.shields.io/npm/dt/@lanonasis/cli)](https://www.npmjs.com/package/@lanonasis/cli)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Golden Contract](https://img.shields.io/badge/Onasis--Core-v0.1%20Compliant-gold)](https://api.lanonasis.com/.well-known/onasis.json)
7
7
 
8
- ๐Ÿš€ **NEW IN v3.0**: Advanced Model Context Protocol (MCP) support with multi-server connections, enhanced error handling, and enterprise-grade transport protocols. Revolutionary interactive CLI experience with guided workflows, smart suggestions, achievement system, and power user mode. Professional command-line interface for LanOnasis Memory as a Service (MaaS) platform with **Golden Contract compliance**.
8
+ ๐ŸŽ‰ **NEW IN v3.9**: Professional CLI UX with seamless inline text editing, intelligent MCP connection management, and first-run onboarding. Advanced Model Context Protocol (MCP) support with multi-server connections, enhanced error handling, and enterprise-grade transport protocols. Revolutionary interactive CLI experience with guided workflows and **Golden Contract compliance**.
9
9
 
10
10
  ## ๐Ÿš€ Quick Start
11
11
 
@@ -34,6 +34,69 @@ onasis health # Verify system health
34
34
  onasis memory create --title "Welcome" --content "My first memory"
35
35
  ```
36
36
 
37
+ ## โœจ Professional CLI UX (v3.9.0+)
38
+
39
+ ### Seamless Inline Text Editing
40
+ No more external editor dependencies! Create and update memories with a professional inline text editor:
41
+
42
+ ```bash
43
+ # Create memory with inline multi-line editor
44
+ onasis memory create --inline
45
+ # Type your content directly in the terminal
46
+ # Use arrow keys to navigate, Enter for new lines
47
+ # Ctrl+D to save, Ctrl+C to cancel
48
+
49
+ # Update existing memory
50
+ onasis memory update <id> --inline
51
+ # Your existing content is preserved and editable
52
+ ```
53
+
54
+ **Features**:
55
+ - โœจ Multi-line editing with visual feedback
56
+ - ๐ŸŽฏ Line numbers and cursor indicators
57
+ - โŒจ๏ธ Full keyboard navigation (arrows, backspace, etc.)
58
+ - ๐Ÿ’พ Auto-preserves existing content when updating
59
+ - ๐Ÿšซ No external editor configuration needed
60
+
61
+ ### Intelligent MCP Connection
62
+ Automatic MCP server discovery and configuration:
63
+
64
+ ```bash
65
+ # Connect to local MCP server (auto-configured)
66
+ onasis mcp connect --local
67
+ # Server path detected automatically
68
+ # Configuration persisted for next time
69
+
70
+ # Check connection status
71
+ onasis mcp status
72
+ ```
73
+
74
+ **Features**:
75
+ - ๐Ÿ” Auto-detects embedded MCP server
76
+ - ๐Ÿ’พ Saves configuration automatically
77
+ - ๐Ÿ”„ Health monitoring and auto-reconnection
78
+ - โœ… Connection verification before operations
79
+ - ๐Ÿ› ๏ธ Clear error messages with fix suggestions
80
+
81
+ ### First-Run Onboarding
82
+ Interactive setup for new users:
83
+
84
+ ```bash
85
+ onasis init
86
+ # Guided setup walks you through:
87
+ # - API configuration
88
+ # - Connectivity testing
89
+ # - Input mode preferences
90
+ # - Troubleshooting if needed
91
+ ```
92
+
93
+ **Features**:
94
+ - ๐Ÿ“‹ Step-by-step guided setup
95
+ - ๐Ÿงช Automatic connectivity tests
96
+ - โš™๏ธ Smart default configuration
97
+ - ๐Ÿ’ก Context-aware troubleshooting
98
+ - ๐Ÿ“– Interactive help and tips
99
+
37
100
  ## ๐Ÿค– Claude Desktop Integration
38
101
 
39
102
  For instant Claude Desktop MCP integration with OAuth2 authentication, see our [Claude Desktop Setup Guide](./CLAUDE_DESKTOP_SETUP.md).
@@ -93,12 +156,14 @@ onasis login --vendor-key <your-vendor-key>
93
156
 
94
157
  ### 2. OAuth Browser Authentication
95
158
 
96
- Secure browser-based authentication:
159
+ Secure browser-based authentication for MCP integration:
97
160
 
98
161
  ```bash
99
162
  onasis login --oauth
100
163
  ```
101
164
 
165
+ > **Note**: OAuth authentication enables MCP integration features (real-time updates, WebSocket connections). For direct CLI memory commands (`memory list`, `memory create`, etc.), use vendor key or credentials authentication.
166
+
102
167
  ### 3. Interactive Credentials
103
168
 
104
169
  Traditional username/password authentication:
@@ -702,23 +702,22 @@ async function handleOAuthFlow(config) {
702
702
  }
703
703
  const tokens = await exchangeCodeForTokens(code, pkce.verifier, authBase, redirectUri);
704
704
  spinner.succeed('Access tokens received');
705
- // Store OAuth tokens - these are already valid auth-gateway tokens from /oauth/token
706
- // No need for additional exchange since /oauth/token returns auth-gateway's own tokens
705
+ // Store OAuth tokens - these are auth-gateway tokens from /oauth/token
706
+ // Note: OAuth tokens are valid for MCP services but not for direct API access
707
707
  await config.setToken(tokens.access_token);
708
708
  await config.set('refresh_token', tokens.refresh_token);
709
709
  await config.set('token_expires_at', Date.now() + (tokens.expires_in * 1000));
710
710
  await config.set('authMethod', 'oauth');
711
- // The OAuth access token from auth-gateway works as the API token for all services
712
- // Store it as the vendor key equivalent for MCP and API access
713
- spinner.text = 'Configuring unified access...';
714
- spinner.start();
715
- // Use the OAuth access token directly - it's already an auth-gateway token
716
- await config.setVendorKey(tokens.access_token);
717
- spinner.succeed('Unified authentication configured');
711
+ spinner.succeed('OAuth tokens stored');
718
712
  console.log();
719
713
  console.log(chalk.green('โœ“ OAuth2 authentication successful'));
720
- console.log(colors.info('You can now use all Lanonasis services'));
721
- console.log(chalk.gray('โœ“ MCP, API, and CLI access configured'));
714
+ console.log(colors.info('You can now use MCP integration features'));
715
+ console.log();
716
+ console.log(chalk.yellow('Note: ') + chalk.gray('OAuth login enables MCP integration.'));
717
+ console.log(chalk.gray('For direct CLI memory commands, use:'));
718
+ console.log(chalk.cyan(' lanonasis auth login --vendor') + chalk.gray(' (get a vendor key from dashboard)'));
719
+ console.log(chalk.gray(' OR'));
720
+ console.log(chalk.cyan(' lanonasis auth login --credentials') + chalk.gray(' (use username/password)'));
722
721
  process.exit(0);
723
722
  }
724
723
  catch (error) {
@@ -1,8 +1,11 @@
1
1
  import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
+ import { dirname, join } from 'path';
3
4
  import { CLIConfig } from '../utils/config.js';
5
+ import { createOnboardingFlow } from '../ux/index.js';
4
6
  export async function initCommand(options) {
5
7
  const config = new CLIConfig();
8
+ await config.init();
6
9
  console.log(chalk.blue.bold('๐Ÿš€ Initializing MaaS CLI'));
7
10
  console.log();
8
11
  // Check if config already exists
@@ -21,6 +24,15 @@ export async function initCommand(options) {
21
24
  return;
22
25
  }
23
26
  }
27
+ const onboardingConfigPath = join(dirname(config.getConfigPath()), 'onboarding.json');
28
+ const onboardingFlow = createOnboardingFlow(onboardingConfigPath);
29
+ const setupResult = await onboardingFlow.runInitialSetup();
30
+ if (!setupResult.completed && setupResult.issues && setupResult.issues.length > 0) {
31
+ console.log(chalk.yellow('โš ๏ธ Onboarding completed with issues:'));
32
+ setupResult.issues.forEach((issue) => console.log(chalk.yellow(` โ€ข ${issue}`)));
33
+ }
34
+ // Reload config after onboarding to preserve generated defaults
35
+ await config.load();
24
36
  // Get configuration
25
37
  const answers = await inquirer.prompt([
26
38
  {
@@ -5,6 +5,8 @@ import { getMCPClient } from '../utils/mcp-client.js';
5
5
  import { EnhancedMCPClient } from '../mcp/client/enhanced-client.js';
6
6
  import { CLIConfig } from '../utils/config.js';
7
7
  import WebSocket from 'ws';
8
+ import { dirname, join } from 'path';
9
+ import { createConnectionManager } from '../ux/index.js';
8
10
  /**
9
11
  * Register MCP-related CLI commands (mcp and mcp-server) on a Commander program.
10
12
  *
@@ -79,6 +81,7 @@ export function mcpCommands(program) {
79
81
  .action(async (options) => {
80
82
  const spinner = ora('Connecting to MCP server...').start();
81
83
  const config = new CLIConfig();
84
+ await config.init();
82
85
  try {
83
86
  let connectionMode;
84
87
  // Determine connection mode - WebSocket takes precedence over remote and local
@@ -112,6 +115,30 @@ export function mcpCommands(program) {
112
115
  let connected = false;
113
116
  // Use Enhanced MCP Client for better connection handling
114
117
  const enhancedClient = new EnhancedMCPClient();
118
+ if (connectionMode === 'local' && !options.localArgs && !options.url) {
119
+ const configDir = dirname(config.getConfigPath());
120
+ const manager = createConnectionManager(join(configDir, 'mcp-config.json'));
121
+ // Initialize manager to load persisted config
122
+ await manager.init();
123
+ if (options.server) {
124
+ await manager.updateConfig({ localServerPath: options.server });
125
+ }
126
+ const result = await manager.connectLocal();
127
+ if (result.success) {
128
+ spinner.succeed(chalk.green('Connected to local MCP server'));
129
+ process.exit(0);
130
+ }
131
+ else {
132
+ spinner.fail(result.error || 'Failed to connect to local MCP server');
133
+ if (result.suggestions && result.suggestions.length > 0) {
134
+ result.suggestions.forEach((suggestion) => {
135
+ console.log(chalk.yellow(` โ€ข ${suggestion}`));
136
+ });
137
+ }
138
+ process.exit(1);
139
+ }
140
+ return;
141
+ }
115
142
  if (options.url) {
116
143
  // Connect to specific URL (WebSocket or remote)
117
144
  const serverConfig = {
@@ -185,12 +212,38 @@ export function mcpCommands(program) {
185
212
  let healthLabel = chalk.gray('Unknown');
186
213
  let healthDetails;
187
214
  let isServiceReachable = false;
215
+ let resolvedHealthUrl;
188
216
  try {
189
217
  const axios = (await import('axios')).default;
190
- // Derive MCP health URL from discovered REST base (e.g. https://mcp.lanonasis.com/api/v1 -> https://mcp.lanonasis.com/health)
191
- const restUrl = config.getMCPRestUrl();
192
- const rootBase = restUrl.replace(/\/api\/v1$/, '');
193
- const healthUrl = `${rootBase}/health`;
218
+ const normalizeMcpHealthUrl = (inputUrl) => {
219
+ const parsed = new URL(inputUrl);
220
+ if (parsed.protocol === 'wss:') {
221
+ parsed.protocol = 'https:';
222
+ }
223
+ else if (parsed.protocol === 'ws:') {
224
+ parsed.protocol = 'http:';
225
+ }
226
+ parsed.pathname = '/health';
227
+ parsed.search = '';
228
+ parsed.hash = '';
229
+ return parsed.toString();
230
+ };
231
+ // Prefer MCP host health based on active mode:
232
+ // - websocket: use configured websocket host (wss -> https)
233
+ // - remote: use configured MCP REST host
234
+ // - local/default: fall back to discovered MCP REST host
235
+ let healthProbeBase;
236
+ if (status.mode === 'websocket') {
237
+ healthProbeBase = config.get('mcpWebSocketUrl') ?? config.getMCPServerUrl();
238
+ }
239
+ else if (status.mode === 'remote') {
240
+ healthProbeBase = config.get('mcpServerUrl') ?? config.getMCPRestUrl();
241
+ }
242
+ else {
243
+ healthProbeBase = config.getMCPRestUrl();
244
+ }
245
+ const healthUrl = normalizeMcpHealthUrl(healthProbeBase);
246
+ resolvedHealthUrl = healthUrl;
194
247
  const token = config.getToken();
195
248
  const vendorKey = await config.getVendorKeyAsync();
196
249
  const headers = {};
@@ -207,7 +260,8 @@ export function mcpCommands(program) {
207
260
  timeout: 5000
208
261
  });
209
262
  const overallStatus = String(response.data?.status ?? '').toLowerCase();
210
- const ok = response.status === 200 && (!overallStatus || overallStatus === 'healthy');
263
+ const okStatuses = new Set(['healthy', 'ok', 'up']);
264
+ const ok = response.status === 200 && (!overallStatus || okStatuses.has(overallStatus));
211
265
  if (ok) {
212
266
  healthLabel = chalk.green('Healthy');
213
267
  isServiceReachable = true;
@@ -258,6 +312,9 @@ export function mcpCommands(program) {
258
312
  if (healthDetails && process.env.CLI_VERBOSE === 'true') {
259
313
  console.log(chalk.gray(`Health details: ${healthDetails}`));
260
314
  }
315
+ if (resolvedHealthUrl && process.env.CLI_VERBOSE === 'true') {
316
+ console.log(chalk.gray(`Health probe URL: ${resolvedHealthUrl}`));
317
+ }
261
318
  // Show features when service is reachable
262
319
  if (isServiceReachable) {
263
320
  if (status.mode === 'remote') {
@@ -6,6 +6,33 @@ import wrap from 'word-wrap';
6
6
  import { format } from 'date-fns';
7
7
  import { apiClient } from '../utils/api.js';
8
8
  import { formatBytes, truncateText } from '../utils/formatting.js';
9
+ import { CLIConfig } from '../utils/config.js';
10
+ import { createTextInputHandler } from '../ux/index.js';
11
+ const resolveInputMode = async () => {
12
+ const config = new CLIConfig();
13
+ await config.init();
14
+ const directMode = config.get('inputMode');
15
+ const userPrefs = config.get('userPreferences');
16
+ const resolved = (directMode || userPrefs?.inputMode);
17
+ return resolved === 'editor' ? 'editor' : 'inline';
18
+ };
19
+ const collectMemoryContent = async (prompt, inputMode, defaultContent) => {
20
+ if (inputMode === 'editor') {
21
+ const { content } = await inquirer.prompt([
22
+ {
23
+ type: 'editor',
24
+ name: 'content',
25
+ message: prompt,
26
+ default: defaultContent,
27
+ },
28
+ ]);
29
+ return content;
30
+ }
31
+ const handler = createTextInputHandler();
32
+ return handler.collectMultilineInput(prompt, {
33
+ defaultContent,
34
+ });
35
+ };
9
36
  export function memoryCommands(program) {
10
37
  // Create memory
11
38
  program
@@ -22,39 +49,42 @@ export function memoryCommands(program) {
22
49
  try {
23
50
  let { title, content, type, tags, topicId, interactive } = options;
24
51
  if (interactive || (!title || !content)) {
52
+ const inputMode = await resolveInputMode();
25
53
  const answers = await inquirer.prompt([
26
54
  {
27
55
  type: 'input',
28
56
  name: 'title',
29
57
  message: 'Memory title:',
30
58
  default: title,
31
- validate: (input) => input.length > 0 || 'Title is required'
32
- },
33
- {
34
- type: 'input',
35
- name: 'content',
36
- message: 'Memory content (or use -c flag for multi-line):',
37
- default: content,
38
- validate: (input) => input.length > 0 || 'Content is required'
59
+ validate: (input) => input.length > 0 || 'Title is required',
39
60
  },
40
61
  {
41
62
  type: 'list',
42
63
  name: 'type',
43
64
  message: 'Memory type:',
44
65
  choices: ['conversation', 'knowledge', 'project', 'context', 'reference'],
45
- default: type || 'context'
66
+ default: type || 'context',
46
67
  },
47
68
  {
48
69
  type: 'input',
49
70
  name: 'tags',
50
71
  message: 'Tags (comma-separated):',
51
- default: tags || ''
52
- }
72
+ default: tags || '',
73
+ },
53
74
  ]);
54
75
  title = answers.title;
55
- content = answers.content;
56
76
  type = answers.type;
57
77
  tags = answers.tags;
78
+ const shouldPromptContent = !content || (interactive && inputMode === 'editor');
79
+ if (shouldPromptContent) {
80
+ content = await collectMemoryContent('Memory content:', inputMode, content);
81
+ }
82
+ }
83
+ if (!title || title.trim().length === 0) {
84
+ throw new Error('Title is required');
85
+ }
86
+ if (!content || content.trim().length === 0) {
87
+ throw new Error('Content is required');
58
88
  }
59
89
  const spinner = ora('Creating memory...').start();
60
90
  const memoryData = {
@@ -275,36 +305,32 @@ export function memoryCommands(program) {
275
305
  const spinner = ora('Fetching current memory...').start();
276
306
  const currentMemory = await apiClient.getMemory(id);
277
307
  spinner.stop();
308
+ const inputMode = await resolveInputMode();
278
309
  const answers = await inquirer.prompt([
279
310
  {
280
311
  type: 'input',
281
312
  name: 'title',
282
313
  message: 'Title:',
283
- default: currentMemory.title
284
- },
285
- {
286
- type: 'editor',
287
- name: 'content',
288
- message: 'Content:',
289
- default: currentMemory.content
314
+ default: currentMemory.title,
290
315
  },
291
316
  {
292
317
  type: 'list',
293
318
  name: 'type',
294
319
  message: 'Memory type:',
295
320
  choices: ['conversation', 'knowledge', 'project', 'context', 'reference'],
296
- default: currentMemory.memory_type
321
+ default: currentMemory.memory_type,
297
322
  },
298
323
  {
299
324
  type: 'input',
300
325
  name: 'tags',
301
326
  message: 'Tags (comma-separated):',
302
- default: currentMemory.tags.join(', ')
303
- }
327
+ default: currentMemory.tags?.join(', ') || '',
328
+ },
304
329
  ]);
330
+ const content = await collectMemoryContent('Content:', inputMode, currentMemory.content);
305
331
  updateData = {
306
332
  title: answers.title,
307
- content: answers.content,
333
+ content,
308
334
  memory_type: answers.type,
309
335
  tags: answers.tags.split(',').map((tag) => tag.trim()).filter(Boolean)
310
336
  };
package/dist/index.js CHANGED
@@ -12,6 +12,8 @@ import { mcpCommands } from './commands/mcp.js';
12
12
  import apiKeysCommand from './commands/api-keys.js';
13
13
  import { CLIConfig } from './utils/config.js';
14
14
  import { getMCPClient } from './utils/mcp-client.js';
15
+ import { dirname, join } from 'path';
16
+ import { createOnboardingFlow } from './ux/index.js';
15
17
  // Load environment variables
16
18
  config();
17
19
  import { createRequire } from 'module';
@@ -51,6 +53,24 @@ program
51
53
  process.env.MEMORY_API_URL = opts.apiUrl;
52
54
  }
53
55
  process.env.CLI_OUTPUT_FORMAT = opts.output;
56
+ const skipOnboarding = actionCommand.name() === 'init' ||
57
+ actionCommand.name() === 'auth' ||
58
+ actionCommand.parent?.name?.() === 'auth';
59
+ if (!skipOnboarding) {
60
+ try {
61
+ const onboardingConfigPath = join(dirname(cliConfig.getConfigPath()), 'onboarding.json');
62
+ const onboardingFlow = createOnboardingFlow(onboardingConfigPath);
63
+ if (onboardingFlow.detectFirstRun()) {
64
+ await onboardingFlow.runInitialSetup();
65
+ }
66
+ }
67
+ catch (error) {
68
+ if (process.env.CLI_VERBOSE === 'true') {
69
+ console.log(colors.warning('Onboarding skipped due to error'));
70
+ console.log(colors.muted(`Error: ${error instanceof Error ? error.message : String(error)}`));
71
+ }
72
+ }
73
+ }
54
74
  // Auto-initialize MCP unless disabled
55
75
  const isMcpFlow = actionCommand.name() === 'mcp' ||
56
76
  actionCommand.parent?.name?.() === 'mcp' ||