morpheus-cli 0.1.4 → 0.1.6

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 (40) hide show
  1. package/README.md +266 -211
  2. package/bin/morpheus.js +30 -0
  3. package/dist/channels/telegram.js +11 -3
  4. package/dist/cli/commands/config.js +18 -3
  5. package/dist/cli/commands/init.js +3 -3
  6. package/dist/cli/index.js +4 -0
  7. package/dist/config/mcp-loader.js +42 -0
  8. package/dist/config/schemas.js +16 -0
  9. package/dist/http/__tests__/auth.test.js +53 -0
  10. package/dist/http/api.js +12 -0
  11. package/dist/http/middleware/auth.js +23 -0
  12. package/dist/http/server.js +2 -1
  13. package/dist/runtime/__tests__/agent.test.js +8 -4
  14. package/dist/runtime/__tests__/agent_memory_limit.test.js +61 -0
  15. package/dist/runtime/__tests__/agent_persistence.test.js +1 -1
  16. package/dist/runtime/__tests__/manual_start_verify.js +4 -0
  17. package/dist/runtime/agent.js +84 -9
  18. package/dist/runtime/audio-agent.js +11 -1
  19. package/dist/runtime/display.js +12 -0
  20. package/dist/runtime/memory/sqlite.js +142 -9
  21. package/dist/runtime/providers/factory.js +50 -6
  22. package/dist/runtime/scaffold.js +5 -0
  23. package/dist/runtime/tools/__tests__/factory.test.js +42 -0
  24. package/dist/runtime/tools/__tests__/tools.test.js +127 -0
  25. package/dist/runtime/tools/analytics-tools.js +73 -0
  26. package/dist/runtime/tools/config-tools.js +70 -0
  27. package/dist/runtime/tools/diagnostic-tools.js +124 -0
  28. package/dist/runtime/tools/factory.js +78 -0
  29. package/dist/runtime/tools/index.js +4 -0
  30. package/dist/types/auth.js +4 -0
  31. package/dist/types/config.js +4 -0
  32. package/dist/types/mcp.js +11 -0
  33. package/dist/types/tools.js +2 -0
  34. package/dist/types/usage.js +1 -0
  35. package/dist/ui/assets/index-4kQpg2wK.js +50 -0
  36. package/dist/ui/assets/index-CwL7mn36.css +1 -0
  37. package/dist/ui/index.html +2 -2
  38. package/package.json +5 -2
  39. package/dist/ui/assets/index-Az60Fu0M.js +0 -50
  40. package/dist/ui/assets/index-nNle8n-Z.css +0 -1
package/README.md CHANGED
@@ -1,211 +1,266 @@
1
- <div align="center">
2
- <img src="./assets/logo.png" alt="Morpheus Logo" width="220" />
3
- </div>
4
-
5
- # Morpheus
6
-
7
- > **Morpheus is a local-first AI operator that bridges developers and machines.**
8
-
9
- Morpheus is a local AI agent for developers, running as a CLI daemon that connects to **LLMs**, **local tools**, and **MCPs**, enabling interaction via **Terminal, Telegram, and Discord**. Inspired by the character Morpheus from *The Matrix*, the project acts as an **intelligent orchestrator**, bridging the gap between the developer and complex systems.
10
-
11
- ## Installation
12
-
13
- Install Morpheus globally via npm:
14
-
15
- ```bash
16
- npm install -g morpheus-cli
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ### 1. Initialize
22
-
23
- Set up your configuration (API keys, preferences):
24
-
25
- ```bash
26
- morpheus init
27
- ```
28
-
29
- ### 2. Start the Agent
30
-
31
- Run the background daemon and Web UI:
32
-
33
- ```bash
34
- morpheus start
35
- ```
36
-
37
- This will:
38
- - Start the agent process
39
- - Launch the Web UI at http://localhost:3333
40
-
41
- ### Other Commands
42
-
43
- ```bash
44
- # Check if Morpheus is running
45
- morpheus status
46
-
47
- # Stop the agent
48
- morpheus stop
49
-
50
- # Diagnose issues
51
- morpheus doctor
52
- ```
53
-
54
- ## Troubleshooting
55
-
56
- ### Command not found
57
-
58
- If you installed successfully but can't run the `morpheus` command:
59
-
60
- 1. **Check your PATH**: Ensure your global npm bin directory is in your system PATH.
61
- - Run `npm bin -g` to see the folder.
62
- - On Windows, this is usually `%APPDATA%\npm`.
63
- - On Linux/Mac, verify `echo $PATH`.
64
- 2. **Restart Terminal**: New installations might not be visible until you restart your shell.
65
-
66
- ## Using NPX
67
- You can run Morpheus without installing it globally using `npx`:
68
-
69
- ```bash
70
-
71
- npx morpheus-cli init
72
-
73
- npx morpheus-cli start
74
-
75
- ```
76
-
77
- ## Technical Overview
78
-
79
- Morpheus is built with **Node.js** and **TypeScript**, using **LangChain** as the orchestration engine. It runs as a background daemon process, managing connections to LLM providers (OpenAI, Anthropic, Ollama) and external channels (Telegram, Discord).
80
-
81
- ### Core Components
82
-
83
- - **Runtime (`src/runtime/`)**: The heart of the application. Manages the agent lifecycle, provider instantiation, and command execution.
84
- - **CLI (`src/cli/`)**: Built with `commander`, handles user interaction, configuration, and daemon control (`start`, `stop`, `status`).
85
- - **Configuration (`src/config/`)**: Singleton-based configuration manager using `zod` for validation and `js-yaml` for persistence (`~/.morpheus/config.yaml`).
86
- - **Channels (`src/channels/`)**: Adapters for external communication. Currently supports Telegram (`telegraf`) with strict user whitelisting.
87
-
88
- ## Features
89
-
90
- ### 🎙️ Audio Transcription (Telegram)
91
- Send voice messages directly to the Telegram bot. Morpheus will:
92
- 1. Transcribe the audio using **Google Gemini**.
93
- 2. Process the text as a standard prompt.
94
- 3. Reply with the answer.
95
-
96
- *Requires a Google Gemini API Key.*
97
-
98
- ## Development Setup
99
-
100
- This guide is for developers contributing to the Morpheus codebase.
101
-
102
- ### Prerequisites
103
-
104
- - **Node.js**: >= 18.x
105
- - **npm**: >= 9.x
106
- - **TypeScript**: >= 5.x
107
-
108
- ### 1. Clone & Install
109
-
110
- ```bash
111
- git clone https://github.com/your-org/morpheus.git
112
- cd morpheus
113
- npm install
114
- ```
115
-
116
- ### 2. Build
117
-
118
- Compile TypeScript source to `dist/`.
119
-
120
- ```bash
121
- npm run build
122
- ```
123
-
124
- ### 3. Run the CLI
125
-
126
- You can run the CLI directly from the source using `npm start`.
127
-
128
- ```bash
129
- # Initialize configuration (creates ~/.morpheus)
130
- npm start -- init
131
-
132
- # Start the daemon
133
- npm start -- start
134
-
135
- # Check status
136
- npm start -- status
137
- ```
138
-
139
- ### 4. Configuration
140
-
141
- The configuration file is located at `~/.morpheus/config.yaml`. You can edit it manually or use the `morpheus config` command.
142
-
143
- ```yaml
144
- agent:
145
- name: "Morpheus"
146
- personality: "stoic, wise, and helpful"
147
- llm:
148
- provider: "openai" # options: openai, anthropic, ollama
149
- model: "gpt-4-turbo"
150
- temperature: 0.7
151
- api_key: "sk-..."
152
- channels:
153
- telegram:
154
- enabled: true
155
- token: "YOUR_TELEGRAM_BOT_TOKEN"
156
- allowedUsers: ["123456789"] # Your Telegram User ID
157
-
158
- # Audio Transcription Support
159
- audio:
160
- enabled: true
161
- apiKey: "YOUR_GEMINI_API_KEY" # Optional if llm.provider is 'gemini'
162
- maxDurationSeconds: 300
163
- ```
164
-
165
- ## Testing
166
-
167
- We use **Vitest** for testing.
168
-
169
- ```bash
170
- # Run unit tests
171
- npm test
172
-
173
- # Run tests in watch mode
174
- npm run test:watch
175
- ```
176
-
177
- ## Project Structure
178
-
179
- ```text
180
- .
181
- ├── assets/ # Static assets
182
- ├── bin/ # CLI entry point (morpheus.js)
183
- ├── specs/ # Technical specifications & documentation
184
- ├── src/
185
- │ ├── channels/ # Communication adapters (Telegram, etc.)
186
- │ ├── cli/ # CLI commands and logic
187
- │ ├── config/ # Configuration management
188
- │ ├── runtime/ # Core agent logic, lifecycle, and providers
189
- │ ├── types/ # Shared TypeScript definitions
190
- │ └── index.ts
191
- └── package.json
192
- ```
193
-
194
- ## Roadmap
195
-
196
- - [ ] **MCP Support**: Full integration with Model Context Protocol.
197
- - [ ] **Discord Adapter**: Support for Discord interactions.
198
- - [ ] **Web Dashboard**: Local UI for management and logs.
199
- - [ ] **Plugin System**: Extend functionality via external modules.
200
-
201
- ## Contributing
202
-
203
- 1. Fork the repository.
204
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`).
205
- 3. Commit your changes (`git commit -m 'feat: Add amazing feature'`).
206
- 4. Push to the branch (`git push origin feature/amazing-feature`).
207
- 5. Open a Pull Request.
208
-
209
- ## License
210
-
211
- MIT
1
+ <div align="center">
2
+ <img src="./assets/logo.png" alt="Morpheus Logo" width="220" />
3
+ </div>
4
+
5
+ # Morpheus
6
+
7
+ > **Morpheus is a local-first AI operator that bridges developers and machines.**
8
+
9
+ Morpheus is a local AI agent for developers, running as a CLI daemon that connects to **LLMs**, **local tools**, and **MCPs**, enabling interaction via **Terminal, Telegram, and Discord**. Inspired by the character Morpheus from *The Matrix*, the project acts as an **intelligent orchestrator**, bridging the gap between the developer and complex systems.
10
+
11
+ ## Installation
12
+
13
+ Install Morpheus globally via npm:
14
+
15
+ ```bash
16
+ npm install -g morpheus-cli
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### 1. Initialize
22
+
23
+ Set up your configuration (API keys, preferences):
24
+
25
+ ```bash
26
+ morpheus init
27
+ ```
28
+
29
+ ### 2. Start the Agent
30
+
31
+ Run the background daemon and Web UI:
32
+
33
+ ```bash
34
+ morpheus start
35
+ ```
36
+
37
+ This will:
38
+ - Start the agent process
39
+ - Launch the Web UI at http://localhost:3333
40
+
41
+ ### Other Commands
42
+
43
+ ```bash
44
+ # Check if Morpheus is running
45
+ morpheus status
46
+
47
+ # Stop the agent
48
+ morpheus stop
49
+
50
+ # Diagnose issues
51
+ morpheus doctor
52
+ ```
53
+
54
+ ## Troubleshooting
55
+
56
+ ### Command not found
57
+
58
+ If you installed successfully but can't run the `morpheus` command:
59
+
60
+ 1. **Check your PATH**: Ensure your global npm bin directory is in your system PATH.
61
+ - Run `npm bin -g` to see the folder.
62
+ - On Windows, this is usually `%APPDATA%\npm`.
63
+ - On Linux/Mac, verify `echo $PATH`.
64
+ 2. **Restart Terminal**: New installations might not be visible until you restart your shell.
65
+
66
+ ## Using NPX
67
+ You can run Morpheus without installing it globally using `npx`:
68
+
69
+ ```bash
70
+
71
+ npx morpheus-cli init
72
+
73
+ npx morpheus-cli start
74
+
75
+ ```
76
+
77
+ ## Technical Overview
78
+
79
+ Morpheus is built with **Node.js** and **TypeScript**, using **LangChain** as the orchestration engine. It runs as a background daemon process, managing connections to LLM providers (OpenAI, Anthropic, Ollama) and external channels (Telegram, Discord).
80
+
81
+ ### Core Components
82
+
83
+ - **Runtime (`src/runtime/`)**: The heart of the application. Manages the agent lifecycle, provider instantiation, and command execution.
84
+ - **CLI (`src/cli/`)**: Built with `commander`, handles user interaction, configuration, and daemon control (`start`, `stop`, `status`).
85
+ - **Configuration (`src/config/`)**: Singleton-based configuration manager using `zod` for validation and `js-yaml` for persistence (`~/.morpheus/config.yaml`).
86
+ - **Channels (`src/channels/`)**: Adapters for external communication. Currently supports Telegram (`telegraf`) with strict user whitelisting.
87
+
88
+ ## Features
89
+
90
+ ### 🖥️ Web Dashboard
91
+ Local React-based UI to manage recordings, chat history, and system status across your agent instances.
92
+
93
+ #### 🔒 UI Authentication
94
+ To protect your Web UI, use the `THE_ARCHITECT_PASS` environment variable. This ensures only authorized users can access the dashboard and API.
95
+
96
+ **Option 1: Using a `.env` file**
97
+ Create a `.env` file in the root of your project:
98
+
99
+ ```env
100
+ THE_ARCHITECT_PASS="your-secure-password"
101
+ ```
102
+
103
+ **Option 2: Using Shell export**
104
+
105
+ ```bash
106
+ export THE_ARCHITECT_PASS="your-secure-password"
107
+ morpheus start
108
+ ```
109
+
110
+ When enabled:
111
+ - The Web UI will redirect to a Login page.
112
+ - API requests require the `x-architect-pass` header.
113
+ - The session is persisted locally in your browser.
114
+
115
+ ### 🧩 MCP Support (Model Context Protocol)
116
+ Full integration with [Model Context Protocol](https://modelcontextprotocol.io/), allowing Morpheus to use standardized tools from any MCP-compatible server.
117
+
118
+ ### 🎙️ Audio Transcription (Telegram)
119
+ Send voice messages directly to the Telegram bot. Morpheus will:
120
+ 1. Transcribe the audio using **Google Gemini**.
121
+ 2. Process the text as a standard prompt.
122
+ 3. Reply with the answer.
123
+
124
+ *Requires a Google Gemini API Key.*
125
+
126
+ ## Development Setup
127
+
128
+ This guide is for developers contributing to the Morpheus codebase.
129
+
130
+ ### Prerequisites
131
+
132
+ - **Node.js**: >= 18.x
133
+ - **npm**: >= 9.x
134
+ - **TypeScript**: >= 5.x
135
+
136
+ ### 1. Clone & Install
137
+
138
+ ```bash
139
+ git clone https://github.com/your-org/morpheus.git
140
+ cd morpheus
141
+ npm install
142
+ ```
143
+
144
+ ### 2. Build
145
+
146
+ Compile TypeScript source to `dist/` and build the Web UI.
147
+
148
+ ```bash
149
+ npm run build
150
+ ```
151
+
152
+ ### 3. Run the CLI
153
+
154
+ You can run the CLI directly from the source using `npm start`.
155
+
156
+ ```bash
157
+ # Initialize configuration (creates ~/.morpheus)
158
+ npm start -- init
159
+
160
+ # Start the daemon
161
+ npm start -- start
162
+
163
+ # Check status
164
+ npm start -- status
165
+ ```
166
+
167
+ ### 4. Configuration
168
+
169
+ The configuration file is located at `~/.morpheus/config.yaml`. You can edit it manually or use the `morpheus config` command.
170
+
171
+ ```yaml
172
+ agent:
173
+ name: "Morpheus"
174
+ personality: "stoic, wise, and helpful"
175
+ llm:
176
+ provider: "openai" # options: openai, anthropic, ollama, gemini
177
+ model: "gpt-4-turbo"
178
+ temperature: 0.7
179
+ api_key: "sk-..."
180
+ memory:
181
+ limit: 100 # Number of messages to retain in context
182
+ channels:
183
+ telegram:
184
+ enabled: true
185
+ token: "YOUR_TELEGRAM_BOT_TOKEN"
186
+ allowedUsers: ["123456789"] # Your Telegram User ID
187
+ discord:
188
+ enabled: false # Coming soon
189
+
190
+ # Web UI Dashboard
191
+ ui:
192
+ enabled: true
193
+ port: 3333
194
+
195
+ # Audio Transcription Support
196
+ audio:
197
+ enabled: true
198
+ apiKey: "YOUR_GEMINI_API_KEY" # Optional if llm.provider is 'gemini'
199
+ maxDurationSeconds: 300
200
+ ```
201
+
202
+ ### 5. MCP Configuration
203
+
204
+ Morpheus supports external tools via **MCP (Model Context Protocol)**. Configure your MCP servers in `~/.morpheus/mcps.json`:
205
+
206
+ ```json
207
+ {
208
+ "coolify": {
209
+ "transport": "stdio",
210
+ "command": "npx",
211
+ "args": ["-y", "@coolify/mcp-server"],
212
+ "env": {
213
+ "COOLIFY_URL": "https://app.coolify.io",
214
+ "COOLIFY_TOKEN": "your-token"
215
+ }
216
+ }
217
+ }
218
+ ```
219
+
220
+ ## Testing
221
+
222
+ We use **Vitest** for testing.
223
+
224
+ ```bash
225
+ # Run unit tests
226
+ npm test
227
+
228
+ # Run tests in watch mode
229
+ npm run test:watch
230
+ ```
231
+
232
+ ## Project Structure
233
+
234
+ ```text
235
+ .
236
+ ├── assets/ # Static assets
237
+ ├── bin/ # CLI entry point (morpheus.js)
238
+ ├── specs/ # Technical specifications & documentation
239
+ ├── src/
240
+ │ ├── channels/ # Communication adapters (Telegram, etc.)
241
+ │ ├── cli/ # CLI commands and logic
242
+ │ ├── config/ # Configuration management
243
+ │ ├── runtime/ # Core agent logic, lifecycle, and providers
244
+ │ ├── types/ # Shared TypeScript definitions
245
+ │ └── ui/ # React Web UI Dashboard
246
+ └── package.json
247
+ ```
248
+
249
+ ## Roadmap
250
+
251
+ - [x] **Web Dashboard**: Local UI for management and logs.
252
+ - [x] **MCP Support**: Full integration with Model Context Protocol.
253
+ - [ ] **Discord Adapter**: Support for Discord interactions.
254
+ - [ ] **Plugin System**: Extend functionality via external modules.
255
+
256
+ ## Contributing
257
+
258
+ 1. Fork the repository.
259
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`).
260
+ 3. Commit your changes (`git commit -m 'feat: Add amazing feature'`).
261
+ 4. Push to the branch (`git push origin feature/amazing-feature`).
262
+ 5. Open a Pull Request.
263
+
264
+ ## License
265
+
266
+ MIT
package/bin/morpheus.js CHANGED
@@ -1,4 +1,34 @@
1
1
  #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ // Load .env file if it exists (Simple shim to avoid 'dotenv' dependency issues)
6
+ const envPath = path.join(process.cwd(), '.env');
7
+ if (fs.existsSync(envPath)) {
8
+ try {
9
+ const envConfig = fs.readFileSync(envPath, 'utf-8');
10
+ envConfig.split('\n').forEach(line => {
11
+ const trimmed = line.trim();
12
+ if (!trimmed || trimmed.startsWith('#')) return;
13
+
14
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
15
+ if (match) {
16
+ const key = match[1].trim();
17
+ let value = match[2].trim();
18
+ // Remove quotes if present
19
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
20
+ value = value.slice(1, -1);
21
+ }
22
+ // Don't overwrite existing env vars
23
+ if (!process.env[key]) {
24
+ process.env[key] = value;
25
+ }
26
+ }
27
+ });
28
+ } catch (err) {
29
+ // Ignore .env errors
30
+ }
31
+ }
2
32
 
3
33
  // Suppress experimental warnings for JSON modules
4
34
  const originalEmit = process.emit;
@@ -87,15 +87,16 @@ export class TelegramAdapter {
87
87
  }
88
88
  this.display.log(`Receiving voice message from @${user} (${duration}s)...`, { source: 'AgentAudio' });
89
89
  let filePath = null;
90
+ let listeningMsg = null;
90
91
  try {
91
- await ctx.sendChatAction('typing');
92
+ listeningMsg = await ctx.reply("🎧Escutando...");
92
93
  // Download
93
94
  this.display.log(`Downloading audio for @${user}...`, { source: 'AgentAudio' });
94
95
  const fileLink = await ctx.telegram.getFileLink(ctx.message.voice.file_id);
95
96
  filePath = await this.downloadToTemp(fileLink);
96
97
  // Transcribe
97
98
  this.display.log(`Transcribing audio for @${user}...`, { source: 'AgentAudio' });
98
- const text = await this.audioAgent.transcribe(filePath, 'audio/ogg', apiKey);
99
+ const { text, usage } = await this.audioAgent.transcribe(filePath, 'audio/ogg', apiKey);
99
100
  this.display.log(`Transcription success for @${user}: "${text}"`, { source: 'AgentAudio', level: 'success' });
100
101
  // Reply with transcription (optional, maybe just process it?)
101
102
  // The prompt says "reply with the answer".
@@ -104,7 +105,14 @@ export class TelegramAdapter {
104
105
  await ctx.reply(`🎤 *Transcription*: _"${text}"_`, { parse_mode: 'Markdown' });
105
106
  await ctx.sendChatAction('typing');
106
107
  // Process with Agent
107
- const response = await this.agent.chat(text);
108
+ const response = await this.agent.chat(text, usage);
109
+ // if (listeningMsg) {
110
+ // try {
111
+ // await ctx.telegram.deleteMessage(ctx.chat.id, listeningMsg.message_id);
112
+ // } catch (e) {
113
+ // // Ignore delete error
114
+ // }
115
+ // }
108
116
  if (response) {
109
117
  await ctx.reply(response);
110
118
  this.display.log(`Responded to @${user} (via audio)`, { source: 'Telegram' });
@@ -22,18 +22,33 @@ export const configCommand = new Command('config')
22
22
  try {
23
23
  await scaffold(); // Ensure config exits
24
24
  if (options.edit) {
25
- console.log(chalk.cyan(`Opening config file: ${PATHS.config}`));
25
+ console.log(chalk.cyan(`Opening configuration files...`));
26
26
  await open(PATHS.config);
27
+ if (await fs.pathExists(PATHS.mcps)) {
28
+ await open(PATHS.mcps);
29
+ }
27
30
  }
28
31
  else {
29
- console.log(chalk.bold('Configuration File:'), chalk.cyan(PATHS.config));
32
+ // Show config.yaml
33
+ console.log(chalk.bold('Main Configuration:'), chalk.cyan(PATHS.config));
30
34
  console.log(chalk.gray('---'));
31
35
  if (await fs.pathExists(PATHS.config)) {
32
36
  const content = await fs.readFile(PATHS.config, 'utf8');
33
37
  console.log(content);
34
38
  }
35
39
  else {
36
- console.log(chalk.yellow('Config file not found (scaffold should have created it).'));
40
+ console.log(chalk.yellow('Config file not found.'));
41
+ }
42
+ console.log(chalk.gray('---\n'));
43
+ // Show mcps.json
44
+ console.log(chalk.bold('MCP Configuration:'), chalk.cyan(PATHS.mcps));
45
+ console.log(chalk.gray('---'));
46
+ if (await fs.pathExists(PATHS.mcps)) {
47
+ const content = await fs.readFile(PATHS.mcps, 'utf8');
48
+ console.log(content);
49
+ }
50
+ else {
51
+ console.log(chalk.yellow('MCP config file not found (create it to add tools).'));
37
52
  }
38
53
  console.log(chalk.gray('---'));
39
54
  }
@@ -4,7 +4,7 @@ import chalk from 'chalk';
4
4
  import { ConfigManager } from '../../config/manager.js';
5
5
  import { renderBanner } from '../utils/render.js';
6
6
  import { DisplayManager } from '../../runtime/display.js';
7
- import { scaffold } from '../../runtime/scaffold.js';
7
+ // import { scaffold } from '../../runtime/scaffold.js';
8
8
  export const initCommand = new Command('init')
9
9
  .description('Initialize Morpheus configuration')
10
10
  .action(async () => {
@@ -12,8 +12,8 @@ export const initCommand = new Command('init')
12
12
  renderBanner();
13
13
  const configManager = ConfigManager.getInstance();
14
14
  const currentConfig = await configManager.load();
15
- // Ensure directory exists
16
- await scaffold();
15
+ // Ensure directory exists - Handled by preAction hook
16
+ // await scaffold();
17
17
  display.log(chalk.blue('Let\'s set up your Morpheus agent!'));
18
18
  try {
19
19
  const name = await input({
package/dist/cli/index.js CHANGED
@@ -8,6 +8,7 @@ import { statusCommand } from './commands/status.js';
8
8
  import { configCommand } from './commands/config.js';
9
9
  import { doctorCommand } from './commands/doctor.js';
10
10
  import { initCommand } from './commands/init.js';
11
+ import { scaffold } from '../runtime/scaffold.js';
11
12
  // Helper to read package.json version
12
13
  const getVersion = () => {
13
14
  try {
@@ -28,6 +29,9 @@ export async function cli() {
28
29
  .name('morpheus')
29
30
  .description('Morpheus CLI Agent')
30
31
  .version(getVersion());
32
+ program.hook('preAction', async () => {
33
+ await scaffold();
34
+ });
31
35
  program.addCommand(initCommand);
32
36
  program.addCommand(startCommand);
33
37
  program.addCommand(stopCommand);
@@ -0,0 +1,42 @@
1
+ import fs from 'fs-extra';
2
+ import { z } from 'zod';
3
+ import { PATHS } from './paths.js';
4
+ import { MCPServerConfigSchema } from './schemas.js';
5
+ import { DisplayManager } from '../runtime/display.js';
6
+ export async function loadMCPConfig() {
7
+ const display = DisplayManager.getInstance();
8
+ const servers = {};
9
+ if (!await fs.pathExists(PATHS.mcps)) {
10
+ return servers;
11
+ }
12
+ let rawConfig;
13
+ try {
14
+ const content = await fs.readFile(PATHS.mcps, 'utf-8');
15
+ if (!content.trim())
16
+ return servers; // Handle empty file
17
+ rawConfig = JSON.parse(content);
18
+ }
19
+ catch (err) {
20
+ display.log(`Failed to parse mcps.json: ${err.message}`, { level: 'error', source: 'Config' });
21
+ return servers;
22
+ }
23
+ // Filter metadata keys (starting with _ or $) and the "example" template key
24
+ const entries = Object.entries(rawConfig).filter(([key]) => !key.startsWith('_') && !key.startsWith('$') && key !== 'example');
25
+ for (const [name, config] of entries) {
26
+ try {
27
+ const validated = MCPServerConfigSchema.parse(config);
28
+ servers[name] = validated;
29
+ display.log(`Loaded MCP server: ${name}`, { level: 'debug', source: 'Config' });
30
+ }
31
+ catch (err) {
32
+ if (err instanceof z.ZodError) {
33
+ const issues = err.issues.map(i => i.message).join(', ');
34
+ display.log(`Invalid MCP server '${name}': ${issues}`, { level: 'warning', source: 'Config' });
35
+ }
36
+ else {
37
+ display.log(`Invalid MCP server '${name}': ${err.message}`, { level: 'warning', source: 'Config' });
38
+ }
39
+ }
40
+ }
41
+ return servers;
42
+ }