@lanonasis/cli 3.8.0 โ†’ 3.9.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +195 -0
  2. package/README.md +65 -2
  3. package/dist/commands/auth.js +1 -1
  4. package/dist/commands/config.js +3 -2
  5. package/dist/commands/init.js +12 -0
  6. package/dist/commands/mcp.js +50 -3
  7. package/dist/commands/memory.js +49 -23
  8. package/dist/index.js +20 -0
  9. package/dist/mcp/access-control.js +2 -2
  10. package/dist/mcp/schemas/tool-schemas.d.ts +4 -4
  11. package/dist/mcp/server/lanonasis-server.js +26 -3
  12. package/dist/utils/api.js +10 -10
  13. package/dist/utils/config.js +40 -6
  14. package/dist/utils/mcp-client.d.ts +2 -0
  15. package/dist/utils/mcp-client.js +33 -15
  16. package/dist/ux/implementations/ConnectionManagerImpl.d.ts +72 -0
  17. package/dist/ux/implementations/ConnectionManagerImpl.js +352 -0
  18. package/dist/ux/implementations/OnboardingFlowImpl.d.ts +72 -0
  19. package/dist/ux/implementations/OnboardingFlowImpl.js +415 -0
  20. package/dist/ux/implementations/TextInputHandlerImpl.d.ts +74 -0
  21. package/dist/ux/implementations/TextInputHandlerImpl.js +342 -0
  22. package/dist/ux/implementations/index.d.ts +11 -0
  23. package/dist/ux/implementations/index.js +11 -0
  24. package/dist/ux/index.d.ts +15 -0
  25. package/dist/ux/index.js +22 -0
  26. package/dist/ux/interfaces/ConnectionManager.d.ts +112 -0
  27. package/dist/ux/interfaces/ConnectionManager.js +7 -0
  28. package/dist/ux/interfaces/OnboardingFlow.d.ts +103 -0
  29. package/dist/ux/interfaces/OnboardingFlow.js +7 -0
  30. package/dist/ux/interfaces/TextInputHandler.d.ts +87 -0
  31. package/dist/ux/interfaces/TextInputHandler.js +7 -0
  32. package/dist/ux/interfaces/index.d.ts +10 -0
  33. package/dist/ux/interfaces/index.js +8 -0
  34. package/package.json +34 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,195 @@
1
+ # Changelog - @lanonasis/cli
2
+
3
+ ## [3.9.0] - 2026-02-01
4
+
5
+ ### ๐ŸŽจ CLI UX Revolution
6
+
7
+ #### Seamless Multi-Line Text Input
8
+ - **Inline Text Editor**: Professional multi-line text input without external editors
9
+ - Raw terminal mode for keystroke capture
10
+ - Full editing support (arrow keys, backspace, newlines)
11
+ - Visual feedback with line numbers and cursor indicators
12
+ - Submit with Ctrl+D, cancel with Ctrl+C
13
+ - Configurable fallback to external editors
14
+
15
+ #### Intelligent MCP Server Connection Management
16
+ - **Auto-Configuration**: Automatically detects and configures embedded MCP servers
17
+ - **Connection Lifecycle**: Smart server process management with health monitoring
18
+ - **Persistent Configuration**: Saves and loads user preferences across sessions
19
+ - **Connection Verification**: Validates server connectivity before operations
20
+ - **Graceful Error Handling**: Clear error messages with actionable resolution steps
21
+
22
+ #### First-Run Onboarding Experience
23
+ - **Guided Setup**: Interactive onboarding flow for new users
24
+ - **Connectivity Testing**: Automatic testing of API endpoints and services
25
+ - **Smart Defaults**: Configures optimal settings based on environment
26
+ - **User Preferences**: Captures and persists input mode, editor choice, and behavior preferences
27
+ - **Troubleshooting Guidance**: Context-aware help when issues are detected
28
+
29
+ ### ๐Ÿ› Critical Bug Fixes (PR #93)
30
+
31
+ #### P1: Connection Verification False Positive
32
+ - **Issue**: `verifyConnection()` returned `true` even when server was in error/stopped state
33
+ - **Fix**: Added explicit checks for error and stopped states before declaring success
34
+ - **Impact**: Users will now see accurate connection status instead of false positives
35
+
36
+ #### P2: Configuration Not Loaded Before Use
37
+ - **Issue**: `ConnectionManager.init()` method existed but was never called
38
+ - **Fix**: Added `init()` to ConnectionManager interface and call it before `connectLocal()`
39
+ - **Impact**: User configuration is now properly loaded and respected
40
+
41
+ #### P2: Empty Content Overwrites in Inline Updates
42
+ - **Issue**: When updating memories in inline mode, `defaultContent` wasn't passed to TextInputHandler
43
+ - **Fix**: Added `defaultContent` support throughout the text input pipeline
44
+ - **Impact**: Memory updates preserve existing content instead of starting with blank slate
45
+
46
+ ### ๐Ÿงช Testing & Quality
47
+
48
+ - **Comprehensive Test Suite**: 168 passing tests including property-based tests
49
+ - **Zero TypeScript Errors**: All compilation errors resolved
50
+ - **No Regressions**: All existing tests continue to pass
51
+ - **Professional Documentation**: Complete inline documentation and type definitions
52
+
53
+ ### ๐Ÿ“ฆ Package Cleanup
54
+
55
+ - **npmignore**: Excludes test files and development artifacts from published package
56
+ - **Directory Reorganization**: Cleaner structure with examples moved to `docs/examples/`
57
+ - **Build Optimization**: Reduced package size by excluding unnecessary files
58
+
59
+ ### ๐Ÿ”„ Breaking Changes
60
+ None - Fully backward compatible
61
+
62
+ ### ๐Ÿ“ Technical Details
63
+
64
+ **New Implementations**:
65
+ - `TextInputHandlerImpl` ([cli/src/ux/implementations/TextInputHandlerImpl.ts](cli/src/ux/implementations/TextInputHandlerImpl.ts))
66
+ - `ConnectionManagerImpl` ([cli/src/ux/implementations/ConnectionManagerImpl.ts](cli/src/ux/implementations/ConnectionManagerImpl.ts))
67
+ - `OnboardingFlowImpl` ([cli/src/ux/implementations/OnboardingFlowImpl.ts](cli/src/ux/implementations/OnboardingFlowImpl.ts))
68
+
69
+ **Integration Points**:
70
+ - Memory commands now use inline text input by default ([cli/src/commands/memory.ts](cli/src/commands/memory.ts:116-119))
71
+ - MCP connect command uses ConnectionManager ([cli/src/commands/mcp.ts](cli/src/commands/mcp.ts:130-137))
72
+ - Init command includes onboarding flow ([cli/src/commands/init.ts](cli/src/commands/init.ts))
73
+
74
+ ## [3.7.0] - 2025-11-23
75
+
76
+ ### ๐Ÿ” Security Infrastructure Upgrade
77
+
78
+ #### Enhanced API Key Security with SHA-256
79
+ - **Cross-Platform SHA-256 Hashing**: Unified hash utilities for consistent API key hashing across all platforms
80
+ - **Local Hash Implementation**: Isolated hash utilities (`src/utils/hash-utils.ts`) for CLI independence
81
+ - **Double-Hash Prevention**: Smart detection of pre-hashed keys to prevent double-hashing errors
82
+ - **Server-Side Validation**: Constant-time comparison for timing-attack prevention
83
+ - **Future NPM Package Ready**: Designed for eventual migration to `@lanonasis/security` npm package
84
+
85
+ #### Technical Improvements
86
+ - **Build Stability**: Fixed TypeScript `rootDir` compilation errors
87
+ - **Zero Deprecation Warnings**: All dependencies verified for production readiness
88
+ - **Cross-Platform Compatibility**: Node.js crypto for server-side, Web Crypto API fallback for browser contexts
89
+ - **Type Safety**: Full TypeScript support with exported hash types (`ApiKeyHash`, `ApiKey`)
90
+
91
+ #### Hash Utility Functions
92
+ ```typescript
93
+ // Available in CLI
94
+ ensureApiKeyHash(apiKey: string): string // Smart hash normalization
95
+ hashApiKey(apiKey: string): string // SHA-256 hashing
96
+ isSha256Hash(value: string): boolean // Hash detection
97
+ ```
98
+
99
+ ### ๐Ÿ›ก๏ธ Security Features
100
+ - โœ… SHA-256 cryptographic hashing for all API keys
101
+ - โœ… Prevents plaintext key transmission
102
+ - โœ… Constant-time hash comparison
103
+ - โœ… Automatic hash detection and normalization
104
+ - โœ… Compatible with existing vendor key authentication
105
+
106
+ ### ๐Ÿ”„ Breaking Changes
107
+ None - Fully backward compatible
108
+
109
+ ### ๐Ÿ“ฆ Dependencies
110
+ - No new external dependencies
111
+ - Uses native Node.js `crypto` module
112
+ - Clean build with zero deprecation warnings
113
+
114
+ ## [3.0.1] - 2025-10-08
115
+
116
+ ### ๐Ÿš€ Major Version Bump
117
+ This is a major version release (3.0) due to the significant MCP architectural changes and new capabilities that may affect existing integrations.
118
+
119
+ ### ๐ŸŽ‰ Major Features
120
+ *Same as 2.0.9 but republished as 3.0.1 due to npm version conflict*
121
+
122
+ ## [2.0.9] - 2025-10-08 (npm publish conflict)
123
+
124
+ ### ๐ŸŽ‰ Major Features
125
+
126
+ #### Enhanced Model Context Protocol (MCP) Support
127
+ - **Multi-Server Connections**: Connect to multiple MCP servers simultaneously with automatic failover
128
+ - **Advanced Error Handling**: Exponential backoff retry logic and graceful degradation
129
+ - **Health Monitoring**: Automatic health checks with latency tracking and auto-reconnection
130
+ - **Connection Pooling**: Efficient resource management for multiple connections
131
+ - **Tool Chain Execution**: Support for sequential and parallel tool execution
132
+
133
+ #### New MCP Infrastructure
134
+ - **Enhanced MCP Client** (`src/mcp/client/enhanced-client.ts`)
135
+ - Multi-server management with priority-based selection
136
+ - Event-driven architecture with connection status tracking
137
+ - Automatic failover to backup servers
138
+
139
+ - **MCP Server Implementation** (`src/mcp/server/lanonasis-server.ts`)
140
+ - Full MCP protocol compliance (tools, resources, prompts)
141
+ - 16 registered tools for memory, topic, and system operations
142
+ - Resource providers for data access
143
+ - Interactive prompts for user guidance
144
+
145
+ - **Transport Support** (`src/mcp/transports/transport-manager.ts`)
146
+ - StdIO transport for local processes
147
+ - WebSocket transport with auto-reconnection
148
+ - SSE (Server-Sent Events) for streaming
149
+ - Authentication support (Bearer, API Key, Basic)
150
+
151
+ - **Schema Validation** (`src/mcp/schemas/tool-schemas.ts`)
152
+ - Zod-based validation for all MCP tools
153
+ - Type-safe operations with clear error messages
154
+ - Comprehensive schemas for memory, topic, API key, and system operations
155
+
156
+ ### ๐Ÿ›  Technical Improvements
157
+ - **Build System**: New MCP-specific build scripts (`build:mcp`, `dev:mcp`, `test:mcp`)
158
+ - **Module Structure**: Dedicated `/src/mcp/` directory with clean separation of concerns
159
+ - **Error Recovery**: Improved error handling throughout the MCP stack
160
+ - **Type Safety**: Full TypeScript support with proper type definitions
161
+
162
+ ### ๐Ÿ› Bug Fixes
163
+ - Fixed authentication flow issues with proper token validation
164
+ - Resolved double slash URL construction in CLI auth
165
+ - Fixed memory command authentication requirements
166
+ - Corrected error messages to show correct authentication command
167
+
168
+ ### ๐Ÿ“š Documentation
169
+ - Enhanced README with MCP usage examples
170
+ - Added MCP Server Mode documentation
171
+ - Updated command reference with new MCP features
172
+ - Created comprehensive MCP enhancement summary
173
+
174
+ ### โš ๏ธ Breaking Changes
175
+ - HTTP transport temporarily disabled in favor of WebSocket/StdIO (more reliable)
176
+ - Some API key operations pending full implementation
177
+
178
+ ### ๐Ÿ”„ Dependencies
179
+ - Updated to latest @modelcontextprotocol/sdk
180
+ - Added ws for WebSocket support
181
+ - Added zod for schema validation
182
+
183
+ ## [2.0.8] - Previous Version
184
+ - Authentication system improvements
185
+ - CLI guided setup enhancements
186
+ - Performance optimizations
187
+
188
+ ## [2.0.7] - Previous Version
189
+ - Memory management improvements
190
+ - Topic organization features
191
+ - Bug fixes and stability improvements
192
+
193
+ ---
194
+
195
+ 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).
@@ -707,7 +707,7 @@ async function handleOAuthFlow(config) {
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
- await config.set('authMethod', 'oauth2');
710
+ await config.set('authMethod', 'oauth');
711
711
  // The OAuth access token from auth-gateway works as the API token for all services
712
712
  // Store it as the vendor key equivalent for MCP and API access
713
713
  spinner.text = 'Configuring unified access...';
@@ -391,8 +391,9 @@ export function configCommands(program) {
391
391
  else {
392
392
  console.log(chalk.green(' โœ“ Authentication credentials found'));
393
393
  // Validate auth method consistency
394
- if (vendorKey && authMethod !== 'vendor_key') {
395
- console.log(chalk.yellow(' โš  Auth method mismatch (has vendor key but method is not vendor_key)'));
394
+ // Note: OAuth users can have vendorKey stored (for MCP access) while authMethod is 'oauth'
395
+ if (vendorKey && authMethod !== 'vendor_key' && authMethod !== 'oauth') {
396
+ console.log(chalk.yellow(' โš  Auth method mismatch (has vendor key but method is not vendor_key or oauth)'));
396
397
  validation.issues.push('Auth method mismatch');
397
398
  if (options.repair) {
398
399
  config.set('authMethod', 'vendor_key');
@@ -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 = {
@@ -169,6 +196,7 @@ export function mcpCommands(program) {
169
196
  const client = getMCPClient();
170
197
  await client.disconnect();
171
198
  console.log(chalk.green('โœ“ Disconnected from MCP server'));
199
+ process.exit(0);
172
200
  });
173
201
  // Status command
174
202
  mcp.command('status')
@@ -183,6 +211,7 @@ export function mcpCommands(program) {
183
211
  await config.init();
184
212
  let healthLabel = chalk.gray('Unknown');
185
213
  let healthDetails;
214
+ let isServiceReachable = false;
186
215
  try {
187
216
  const axios = (await import('axios')).default;
188
217
  // Derive MCP health URL from discovered REST base (e.g. https://mcp.lanonasis.com/api/v1 -> https://mcp.lanonasis.com/health)
@@ -207,14 +236,17 @@ export function mcpCommands(program) {
207
236
  const overallStatus = String(response.data?.status ?? '').toLowerCase();
208
237
  const ok = response.status === 200 && (!overallStatus || overallStatus === 'healthy');
209
238
  if (ok) {
210
- healthLabel = chalk.green('Reachable');
239
+ healthLabel = chalk.green('Healthy');
240
+ isServiceReachable = true;
211
241
  }
212
242
  else {
213
243
  healthLabel = chalk.yellow('Degraded');
244
+ isServiceReachable = true; // Service is reachable but degraded
214
245
  }
215
246
  }
216
247
  catch (error) {
217
248
  healthLabel = chalk.red('Unreachable');
249
+ isServiceReachable = false;
218
250
  if (error instanceof Error) {
219
251
  healthDetails = error.message;
220
252
  }
@@ -224,7 +256,14 @@ export function mcpCommands(program) {
224
256
  }
225
257
  console.log(chalk.cyan('\n๐Ÿ“Š MCP Connection Status'));
226
258
  console.log(chalk.cyan('========================'));
227
- console.log(`Status: ${status.connected ? chalk.green('Connected') : chalk.red('Disconnected')}`);
259
+ // Show status based on service reachability, not in-memory connection state
260
+ // The CLI isn't a persistent daemon - "connected" means the service is available
261
+ if (isServiceReachable) {
262
+ console.log(`Status: ${chalk.green('Ready')} (service reachable)`);
263
+ }
264
+ else {
265
+ console.log(`Status: ${chalk.red('Unavailable')} (service unreachable)`);
266
+ }
228
267
  // Display mode with proper labels
229
268
  let modeDisplay;
230
269
  switch (status.mode) {
@@ -246,7 +285,8 @@ export function mcpCommands(program) {
246
285
  if (healthDetails && process.env.CLI_VERBOSE === 'true') {
247
286
  console.log(chalk.gray(`Health details: ${healthDetails}`));
248
287
  }
249
- if (status.connected) {
288
+ // Show features when service is reachable
289
+ if (isServiceReachable) {
250
290
  if (status.mode === 'remote') {
251
291
  console.log(`\n${chalk.cyan('Features:')}`);
252
292
  console.log('โ€ข Real-time updates via SSE');
@@ -259,7 +299,12 @@ export function mcpCommands(program) {
259
299
  console.log('โ€ข Authenticated WebSocket connection');
260
300
  console.log('โ€ข Production-ready MCP server');
261
301
  }
302
+ console.log(chalk.green('\nโœ“ MCP tools are available. Run "lanonasis mcp tools" to see them.'));
303
+ }
304
+ else {
305
+ console.log(chalk.yellow('\nโš  MCP service is not reachable. Run "lanonasis mcp diagnose" for troubleshooting.'));
262
306
  }
307
+ process.exit(0);
263
308
  });
264
309
  // List tools command
265
310
  mcp.command('tools')
@@ -456,6 +501,7 @@ export function mcpCommands(program) {
456
501
  console.log(' --prefer-local : Use local stdio mode (development only)');
457
502
  console.log(' --auto : Auto-detect based on configuration (default)');
458
503
  }
504
+ process.exit(0);
459
505
  });
460
506
  // Start MCP server for external clients
461
507
  mcp.command('start')
@@ -790,5 +836,6 @@ export function mcpCommands(program) {
790
836
  console.log(chalk.gray(' โ€ข Verify your network allows outbound HTTPS connections'));
791
837
  console.log(chalk.gray(' โ€ข Contact support if issues persist'));
792
838
  }
839
+ process.exit(0);
793
840
  });
794
841
  }
@@ -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: 'editor',
35
- name: 'content',
36
- message: 'Memory content:',
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' ||
@@ -192,7 +192,7 @@ export class MemoryAccessControl {
192
192
  const apiUrl = this.config.get('apiUrl') || 'https://api.lanonasis.com';
193
193
  const token = this.config.get('token');
194
194
  const axios = (await import('axios')).default;
195
- const response = await axios.get(`${apiUrl}/api/v1/memory/${memoryId}`, {
195
+ const response = await axios.get(`${apiUrl}/api/v1/memories/${memoryId}`, {
196
196
  headers: {
197
197
  'Authorization': `Bearer ${token}`,
198
198
  'Content-Type': 'application/json'
@@ -210,7 +210,7 @@ export class MemoryAccessControl {
210
210
  const apiUrl = this.config.get('apiUrl') || 'https://api.lanonasis.com';
211
211
  const token = this.config.get('token');
212
212
  const axios = (await import('axios')).default;
213
- const response = await axios.get(`${apiUrl}/api/v1/memory?user_id=${userId}`, {
213
+ const response = await axios.get(`${apiUrl}/api/v1/memories?user_id=${userId}`, {
214
214
  headers: {
215
215
  'Authorization': `Bearer ${token}`,
216
216
  'Content-Type': 'application/json'