paean 0.1.3 → 0.2.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/README.md +76 -216
- package/dist/agent/chat.d.ts +22 -0
- package/dist/agent/chat.d.ts.map +1 -0
- package/dist/agent/chat.js +210 -0
- package/dist/agent/chat.js.map +1 -0
- package/dist/agent/index.d.ts +9 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/renderer.d.ts +53 -0
- package/dist/agent/renderer.d.ts.map +1 -0
- package/dist/agent/renderer.js +133 -0
- package/dist/agent/renderer.js.map +1 -0
- package/dist/agent/service.d.ts +42 -0
- package/dist/agent/service.d.ts.map +1 -0
- package/dist/agent/service.js +187 -0
- package/dist/agent/service.js.map +1 -0
- package/dist/agent/types.d.ts +84 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +6 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/api/todo.d.ts +56 -0
- package/dist/api/todo.d.ts.map +1 -1
- package/dist/api/todo.js +60 -1
- package/dist/api/todo.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +14 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent.d.ts +15 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +198 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +98 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +303 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/resources.d.ts.map +1 -1
- package/dist/mcp/resources.js +49 -20
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +253 -12
- package/dist/mcp/tools.js.map +1 -1
- package/package.json +12 -8
package/README.md
CHANGED
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
# Paean CLI
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Claude Code-like AI agent with local MCP integration and task management
|
|
4
4
|
|
|
5
|
-
**Paean CLI** is a
|
|
5
|
+
**Paean CLI** is a powerful command-line AI agent that enables:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- **Task Management**: View, create, complete, and manage tasks from the command line
|
|
13
|
-
- **MCP Server Mode**: Seamless integration with AI coding assistants via MCP protocol
|
|
14
|
-
- **Dual Authentication**: Support for both QR code login and browser-based OAuth
|
|
15
|
-
- **Context Generation**: Generate context files for AI agents with current task information
|
|
16
|
-
- **Project Detection**: Automatically detects project type and associates tasks
|
|
7
|
+
- **🤖 Agent Mode**: Interactive AI chat with streaming responses (default)
|
|
8
|
+
- **🔗 Local MCP Integration**: Connect to local MCP servers for tool calling
|
|
9
|
+
- **📋 Task Management**: View, create, and manage tasks from the command line
|
|
10
|
+
- **🔌 MCP Server Mode**: Act as an MCP server for AI coding assistants
|
|
17
11
|
|
|
18
12
|
## Installation
|
|
19
13
|
|
|
20
14
|
```bash
|
|
21
|
-
# Install globally
|
|
15
|
+
# Install globally with bun (recommended)
|
|
16
|
+
bun add -g paean
|
|
17
|
+
|
|
18
|
+
# Or use npm
|
|
22
19
|
npm install -g paean
|
|
23
20
|
|
|
24
21
|
# Or use directly with npx
|
|
25
|
-
npx paean
|
|
22
|
+
npx paean
|
|
26
23
|
```
|
|
27
24
|
|
|
28
25
|
## Quick Start
|
|
@@ -30,269 +27,132 @@ npx paean --help
|
|
|
30
27
|
### 1. Authenticate
|
|
31
28
|
|
|
32
29
|
```bash
|
|
33
|
-
# Browser-based login (recommended)
|
|
34
30
|
paean login
|
|
35
|
-
|
|
36
|
-
# QR code login (scan with Paean mobile app)
|
|
37
|
-
paean login --qr
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 2. View Tasks
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
# List all tasks
|
|
44
|
-
paean tasks
|
|
45
|
-
|
|
46
|
-
# List pending tasks only
|
|
47
|
-
paean tasks --status pending
|
|
48
|
-
|
|
49
|
-
# Output as JSON
|
|
50
|
-
paean tasks --json
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### 3. Manage Tasks
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
# Create a new task
|
|
57
|
-
paean tasks add "Implement user authentication"
|
|
58
|
-
|
|
59
|
-
# Complete a task
|
|
60
|
-
paean tasks complete <task-id> --summary "Added JWT authentication"
|
|
61
|
-
|
|
62
|
-
# Update task priority
|
|
63
|
-
paean tasks update <task-id> --priority high
|
|
64
31
|
```
|
|
65
32
|
|
|
66
|
-
###
|
|
33
|
+
### 2. Start Agent Mode (Default)
|
|
67
34
|
|
|
68
35
|
```bash
|
|
69
|
-
#
|
|
70
|
-
paean
|
|
71
|
-
|
|
72
|
-
# Output to stdout
|
|
73
|
-
paean context --stdout
|
|
74
|
-
|
|
75
|
-
# Output as JSON
|
|
76
|
-
paean context --json
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## MCP Server Mode
|
|
80
|
-
|
|
81
|
-
Paean CLI can run as an MCP server, allowing AI coding assistants to read tasks and update their status.
|
|
36
|
+
# Just run paean to start interactive AI chat
|
|
37
|
+
paean
|
|
82
38
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Add to your `.cursor/mcp.json`:
|
|
86
|
-
|
|
87
|
-
```json
|
|
88
|
-
{
|
|
89
|
-
"mcpServers": {
|
|
90
|
-
"paean": {
|
|
91
|
-
"command": "npx",
|
|
92
|
-
"args": ["paean", "serve"]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
39
|
+
# Or send a single message
|
|
40
|
+
paean -m "What can you help me with?"
|
|
96
41
|
```
|
|
97
42
|
|
|
98
|
-
###
|
|
43
|
+
### 3. Use with Local MCP Servers
|
|
99
44
|
|
|
100
|
-
|
|
45
|
+
Configure your MCP servers in `~/.paean/mcp_config.json`:
|
|
101
46
|
|
|
102
47
|
```json
|
|
103
48
|
{
|
|
104
49
|
"mcpServers": {
|
|
105
|
-
"
|
|
50
|
+
"vibe_kanban": {
|
|
106
51
|
"command": "npx",
|
|
107
|
-
"args": ["
|
|
52
|
+
"args": ["-y", "vibe-kanban@latest", "--mcp"]
|
|
108
53
|
}
|
|
109
54
|
}
|
|
110
55
|
}
|
|
111
56
|
```
|
|
112
57
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
| Resource URI | Description |
|
|
116
|
-
| ------------------------- | ------------------------------------ |
|
|
117
|
-
| `paean://tasks/pending` | Current pending tasks |
|
|
118
|
-
| `paean://tasks/completed` | Recently completed tasks |
|
|
119
|
-
| `paean://tasks/all` | All tasks with full details |
|
|
120
|
-
| `paean://context` | Full project context |
|
|
121
|
-
| `paean://pending-changes` | AI-suggested changes awaiting review |
|
|
122
|
-
|
|
123
|
-
### Available MCP Tools
|
|
124
|
-
|
|
125
|
-
| Tool | Description |
|
|
126
|
-
| --------------------- | ----------------------------- |
|
|
127
|
-
| `paean_complete_task` | Mark a task as completed |
|
|
128
|
-
| `paean_create_task` | Create a new task |
|
|
129
|
-
| `paean_update_task` | Update task status/priority |
|
|
130
|
-
| `paean_list_tasks` | List tasks with filters |
|
|
131
|
-
| `paean_accept_change` | Accept an AI-suggested change |
|
|
132
|
-
| `paean_reject_change` | Reject an AI-suggested change |
|
|
133
|
-
|
|
134
|
-
## Commands Reference
|
|
135
|
-
|
|
136
|
-
### `paean login`
|
|
137
|
-
|
|
138
|
-
Authenticate with Paean AI.
|
|
58
|
+
Then the agent can use your local tools:
|
|
139
59
|
|
|
140
60
|
```bash
|
|
141
|
-
paean
|
|
142
|
-
|
|
143
|
-
|
|
61
|
+
paean
|
|
62
|
+
You: Create a new task on my kanban board
|
|
63
|
+
Pæan: 🔗 MCP: vibe_kanban → create_task...
|
|
64
|
+
✓ MCP: vibe_kanban → create_task completed
|
|
65
|
+
I've created the task on your kanban board!
|
|
144
66
|
```
|
|
145
67
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Sign out and clear stored credentials.
|
|
68
|
+
## Agent Mode Options
|
|
149
69
|
|
|
150
70
|
```bash
|
|
151
|
-
paean
|
|
152
|
-
paean
|
|
71
|
+
paean # Start interactive mode
|
|
72
|
+
paean --no-mcp # Disable local MCP integration
|
|
73
|
+
paean -d, --debug # Enable debug logging
|
|
74
|
+
paean -m "message" # Send a single message
|
|
153
75
|
```
|
|
154
76
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
View and manage tasks.
|
|
77
|
+
## Task Management
|
|
158
78
|
|
|
159
79
|
```bash
|
|
160
80
|
paean tasks # List all tasks
|
|
161
81
|
paean tasks --status pending # Filter by status
|
|
162
|
-
paean tasks --priority high # Filter by priority
|
|
163
|
-
paean tasks --json # JSON output
|
|
164
|
-
|
|
165
82
|
paean tasks add "Task description" # Create task
|
|
166
83
|
paean tasks complete <id> # Complete task
|
|
167
|
-
paean tasks update <id> # Update task
|
|
168
|
-
paean tasks delete <id> # Delete task
|
|
169
|
-
|
|
170
|
-
paean tasks pending # View AI-suggested changes
|
|
171
|
-
paean tasks accept <changeId> # Accept change
|
|
172
|
-
paean tasks reject <changeId> # Reject change
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### `paean context`
|
|
176
|
-
|
|
177
|
-
Generate context file for AI agents.
|
|
178
|
-
|
|
179
|
-
```bash
|
|
180
|
-
paean context # Write to .paean/context.md
|
|
181
|
-
paean context --output custom.md # Custom output path
|
|
182
|
-
paean context --json # JSON format
|
|
183
|
-
paean context --stdout # Print to stdout
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### `paean serve`
|
|
187
|
-
|
|
188
|
-
Start MCP server for AI agent integration.
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
paean serve # Start MCP server (stdio)
|
|
192
|
-
paean serve --debug # Enable debug logging
|
|
193
84
|
```
|
|
194
85
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
Check if current changes satisfy pending tasks.
|
|
86
|
+
## MCP Server Mode
|
|
198
87
|
|
|
199
|
-
|
|
200
|
-
paean validate # Validate pending tasks
|
|
201
|
-
paean validate --auto-complete # Auto-complete validated tasks
|
|
202
|
-
paean validate --json # JSON output
|
|
203
|
-
```
|
|
88
|
+
Run Paean as an MCP server for Cursor, Claude Desktop, or other AI assistants:
|
|
204
89
|
|
|
205
|
-
|
|
90
|
+
### Configure in Cursor
|
|
206
91
|
|
|
207
|
-
|
|
92
|
+
Add to `.cursor/mcp.json`:
|
|
208
93
|
|
|
209
94
|
```json
|
|
210
95
|
{
|
|
211
|
-
"
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
View config path:
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
paean --config
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Programmatic Usage
|
|
227
|
-
|
|
228
|
-
Paean CLI can also be used as a library:
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
import {
|
|
232
|
-
qrLogin,
|
|
233
|
-
browserLogin,
|
|
234
|
-
getTodoList,
|
|
235
|
-
completeTodoItem,
|
|
236
|
-
startMcpServer,
|
|
237
|
-
} from "paean";
|
|
238
|
-
|
|
239
|
-
// Authenticate
|
|
240
|
-
const result = await browserLogin();
|
|
241
|
-
if (result.success) {
|
|
242
|
-
// Get tasks
|
|
243
|
-
const tasks = await getTodoList({ status: "pending" });
|
|
244
|
-
console.log(tasks.data.items);
|
|
245
|
-
|
|
246
|
-
// Complete a task
|
|
247
|
-
await completeTodoItem("task-id", "Completed the feature");
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"paean": {
|
|
98
|
+
"command": "npx",
|
|
99
|
+
"args": ["paean", "serve"]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
248
102
|
}
|
|
249
103
|
```
|
|
250
104
|
|
|
251
|
-
|
|
105
|
+
### Available MCP Tools
|
|
252
106
|
|
|
253
|
-
|
|
107
|
+
| Tool | Description |
|
|
108
|
+
| --------------------- | ------------------------------- |
|
|
109
|
+
| `paean_list_tasks` | List tasks with filters |
|
|
110
|
+
| `paean_create_task` | Create a new task |
|
|
111
|
+
| `paean_complete_task` | Mark a task as completed |
|
|
112
|
+
| `paean_update_task` | Update task status/priority |
|
|
113
|
+
| `paean_add_subtask` | Add a subtask to an existing |
|
|
254
114
|
|
|
255
|
-
|
|
256
|
-
2. **Worker (Claude/Cursor)**: Reads tasks via MCP, implements changes, reports completion
|
|
257
|
-
3. **Reviewer (Human)**: Reviews AI suggestions and approves changes
|
|
115
|
+
## Architecture
|
|
258
116
|
|
|
259
117
|
```
|
|
260
118
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
261
|
-
│
|
|
262
|
-
│ (
|
|
119
|
+
│ You (CLI) │────▶│ Paean Cloud │────▶│ Pæan Agent │
|
|
120
|
+
│ (Agent Mode) │ │ (zero-api) │ │ (ADK) │
|
|
263
121
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
│
|
|
270
|
-
|
|
271
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
122
|
+
│ │ │
|
|
123
|
+
│ │ SSE: mcp_tool_call │
|
|
124
|
+
▼ ▼ │
|
|
125
|
+
┌─────────────────┐◀────────────────────────────────────┘
|
|
126
|
+
│ Local MCP │
|
|
127
|
+
│ (vibe-kanban) │ ← Executes locally, returns result
|
|
128
|
+
└─────────────────┘
|
|
272
129
|
```
|
|
273
130
|
|
|
274
|
-
##
|
|
131
|
+
## Configuration
|
|
275
132
|
|
|
276
|
-
|
|
277
|
-
- **Localhost Only**: Browser OAuth callbacks only accept localhost URLs
|
|
278
|
-
- **Token-Based**: Uses JWT tokens that expire and can be revoked
|
|
279
|
-
- **No Password Storage**: Passwords are never stored locally
|
|
133
|
+
Config stored in `~/.paean/`:
|
|
280
134
|
|
|
281
|
-
|
|
135
|
+
- `config.json` - Auth and preferences
|
|
136
|
+
- `mcp_config.json` - MCP server configuration
|
|
282
137
|
|
|
283
|
-
|
|
284
|
-
- npm or yarn
|
|
138
|
+
## Programmatic Usage
|
|
285
139
|
|
|
286
|
-
|
|
140
|
+
```typescript
|
|
141
|
+
import { agentService, startChat, McpClient } from 'paean';
|
|
287
142
|
|
|
288
|
-
|
|
143
|
+
// Start interactive chat
|
|
144
|
+
await startChat({ mcpState });
|
|
289
145
|
|
|
290
|
-
|
|
146
|
+
// Or send a single message
|
|
147
|
+
const response = await sendMessage("Hello");
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Requirements
|
|
291
151
|
|
|
292
|
-
|
|
152
|
+
- Node.js 18+ or Bun
|
|
153
|
+
- Paean AI account
|
|
293
154
|
|
|
294
155
|
## Links
|
|
295
156
|
|
|
296
157
|
- **Website**: https://paean.ai
|
|
297
|
-
- **API Documentation**: https://api.paean.ai/docs
|
|
298
158
|
- **Support**: support@paean.ai
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Chat Loop
|
|
3
|
+
* Handles user input and agent responses in a REPL-style interface
|
|
4
|
+
*/
|
|
5
|
+
import type { McpState, McpToolResult } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Chat options
|
|
8
|
+
*/
|
|
9
|
+
export interface ChatOptions {
|
|
10
|
+
mcpState?: McpState;
|
|
11
|
+
onMcpToolCall?: (callId: string, serverName: string, toolName: string, args: Record<string, unknown>) => Promise<McpToolResult>;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Start the interactive chat loop
|
|
16
|
+
*/
|
|
17
|
+
export declare function startChat(options?: ChatOptions): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Send a single message (non-interactive mode)
|
|
20
|
+
*/
|
|
21
|
+
export declare function sendMessage(message: string, options?: ChatOptions): Promise<string>;
|
|
22
|
+
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/agent/chat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAwB,MAAM,YAAY,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,aAAa,CAAC,EAAE,CACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+LxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,WAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAoCjB"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Chat Loop
|
|
3
|
+
* Handles user input and agent responses in a REPL-style interface
|
|
4
|
+
*/
|
|
5
|
+
import * as readline from 'readline';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { agentService } from './service.js';
|
|
8
|
+
import { renderMarkdown, renderToolCall, renderToolResult, renderMcpToolCall, renderMcpToolResult, renderError, renderPrompt, renderAgentLabel, renderWelcome, renderGoodbye, renderThinking, } from './renderer.js';
|
|
9
|
+
/**
|
|
10
|
+
* Start the interactive chat loop
|
|
11
|
+
*/
|
|
12
|
+
export async function startChat(options = {}) {
|
|
13
|
+
const { mcpState, onMcpToolCall, debug } = options;
|
|
14
|
+
// Calculate total MCP tools
|
|
15
|
+
const mcpToolCount = mcpState?.mcpServers?.reduce((sum, server) => sum + (server.tools?.length || 0), 0);
|
|
16
|
+
// Print welcome message
|
|
17
|
+
console.log(renderWelcome(mcpToolCount));
|
|
18
|
+
// Create readline interface
|
|
19
|
+
const rl = readline.createInterface({
|
|
20
|
+
input: process.stdin,
|
|
21
|
+
output: process.stdout,
|
|
22
|
+
terminal: true,
|
|
23
|
+
});
|
|
24
|
+
let conversationId;
|
|
25
|
+
let isProcessing = false;
|
|
26
|
+
let currentStreamAbort = null;
|
|
27
|
+
// Handle Ctrl+C
|
|
28
|
+
const handleSigint = () => {
|
|
29
|
+
if (isProcessing && currentStreamAbort) {
|
|
30
|
+
// Abort current stream
|
|
31
|
+
currentStreamAbort();
|
|
32
|
+
console.log(chalk.dim('\n(interrupted)'));
|
|
33
|
+
isProcessing = false;
|
|
34
|
+
currentStreamAbort = null;
|
|
35
|
+
promptUser();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Exit
|
|
39
|
+
console.log(renderGoodbye());
|
|
40
|
+
rl.close();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
process.on('SIGINT', handleSigint);
|
|
45
|
+
// Prompt for user input
|
|
46
|
+
const promptUser = () => {
|
|
47
|
+
rl.question(renderPrompt(), async (input) => {
|
|
48
|
+
const trimmedInput = input.trim();
|
|
49
|
+
if (!trimmedInput) {
|
|
50
|
+
promptUser();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Handle special commands
|
|
54
|
+
if (trimmedInput === '/exit' || trimmedInput === '/quit') {
|
|
55
|
+
console.log(renderGoodbye());
|
|
56
|
+
rl.close();
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
if (trimmedInput === '/clear') {
|
|
60
|
+
console.clear();
|
|
61
|
+
console.log(renderWelcome(mcpToolCount));
|
|
62
|
+
promptUser();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (trimmedInput === '/help') {
|
|
66
|
+
console.log(chalk.dim(`
|
|
67
|
+
Commands:
|
|
68
|
+
/clear - Clear the screen
|
|
69
|
+
/help - Show this help
|
|
70
|
+
/exit - Exit the chat
|
|
71
|
+
`));
|
|
72
|
+
promptUser();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Process the message
|
|
76
|
+
await processMessage(trimmedInput);
|
|
77
|
+
promptUser();
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
// Process a message
|
|
81
|
+
const processMessage = async (message) => {
|
|
82
|
+
isProcessing = true;
|
|
83
|
+
let responseText = '';
|
|
84
|
+
let hasStartedResponse = false;
|
|
85
|
+
// Print thinking indicator
|
|
86
|
+
process.stdout.write('\n' + renderThinking());
|
|
87
|
+
const callbacks = {
|
|
88
|
+
onContent: (text, partial) => {
|
|
89
|
+
if (!hasStartedResponse) {
|
|
90
|
+
// Clear thinking indicator and print agent label
|
|
91
|
+
process.stdout.write('\r\x1b[K'); // Clear line
|
|
92
|
+
process.stdout.write(renderAgentLabel());
|
|
93
|
+
hasStartedResponse = true;
|
|
94
|
+
}
|
|
95
|
+
if (partial) {
|
|
96
|
+
// Streaming - just append
|
|
97
|
+
responseText += text;
|
|
98
|
+
process.stdout.write(text);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Final content - render as markdown
|
|
102
|
+
responseText = text;
|
|
103
|
+
const rendered = renderMarkdown(text);
|
|
104
|
+
// Clear what we've written and re-render
|
|
105
|
+
process.stdout.write('\r\x1b[K');
|
|
106
|
+
process.stdout.write(renderAgentLabel());
|
|
107
|
+
console.log(rendered.trim());
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
onToolCall: (_id, name) => {
|
|
111
|
+
if (debug) {
|
|
112
|
+
console.log('\n' + renderToolCall(name));
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
onToolResult: (_id, name) => {
|
|
116
|
+
if (debug) {
|
|
117
|
+
console.log(renderToolResult(name));
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
onMcpToolCall: async (callId, serverName, toolName, args) => {
|
|
121
|
+
console.log('\n' + renderMcpToolCall(serverName, toolName));
|
|
122
|
+
if (onMcpToolCall) {
|
|
123
|
+
try {
|
|
124
|
+
const result = await onMcpToolCall(callId, serverName, toolName, args);
|
|
125
|
+
console.log(renderMcpToolResult(serverName, toolName, !result.isError));
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.log(renderMcpToolResult(serverName, toolName, false));
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{ type: 'text', text: `Error: ${error.message}` },
|
|
133
|
+
],
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
content: [{ type: 'text', text: 'MCP not available' }],
|
|
140
|
+
isError: true,
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
onDone: (convId) => {
|
|
144
|
+
conversationId = convId;
|
|
145
|
+
if (!hasStartedResponse) {
|
|
146
|
+
// Clear thinking indicator if no content was received
|
|
147
|
+
process.stdout.write('\r\x1b[K');
|
|
148
|
+
}
|
|
149
|
+
console.log(''); // New line after response
|
|
150
|
+
isProcessing = false;
|
|
151
|
+
currentStreamAbort = null;
|
|
152
|
+
},
|
|
153
|
+
onError: (error) => {
|
|
154
|
+
process.stdout.write('\r\x1b[K'); // Clear line
|
|
155
|
+
console.log(renderError(error));
|
|
156
|
+
isProcessing = false;
|
|
157
|
+
currentStreamAbort = null;
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
try {
|
|
161
|
+
const { abort } = await agentService.streamMessage(message, callbacks, {
|
|
162
|
+
conversationId,
|
|
163
|
+
mcpState,
|
|
164
|
+
});
|
|
165
|
+
currentStreamAbort = abort;
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.log(renderError(error.message));
|
|
169
|
+
isProcessing = false;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
// Start the prompt loop
|
|
173
|
+
promptUser();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Send a single message (non-interactive mode)
|
|
177
|
+
*/
|
|
178
|
+
export async function sendMessage(message, options = {}) {
|
|
179
|
+
const { mcpState, onMcpToolCall } = options;
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
let responseText = '';
|
|
182
|
+
const callbacks = {
|
|
183
|
+
onContent: (text, partial) => {
|
|
184
|
+
if (!partial) {
|
|
185
|
+
responseText = text;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
responseText += text;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
onMcpToolCall: async (callId, serverName, toolName, args) => {
|
|
192
|
+
if (onMcpToolCall) {
|
|
193
|
+
return onMcpToolCall(callId, serverName, toolName, args);
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: 'text', text: 'MCP not available' }],
|
|
197
|
+
isError: true,
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
onDone: () => {
|
|
201
|
+
resolve(responseText);
|
|
202
|
+
},
|
|
203
|
+
onError: (error) => {
|
|
204
|
+
reject(new Error(error));
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
agentService.streamMessage(message, callbacks, { mcpState });
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/agent/chat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Module Index
|
|
3
|
+
* Export agent functionality
|
|
4
|
+
*/
|
|
5
|
+
export { AgentService, agentService } from './service.js';
|
|
6
|
+
export { startChat, sendMessage } from './chat.js';
|
|
7
|
+
export { renderMarkdown, renderToolCall, renderToolResult, renderMcpToolCall, renderMcpToolResult, renderError, renderSuccess, renderPrompt, renderAgentLabel, renderWelcome, renderGoodbye, renderThinking, } from './renderer.js';
|
|
8
|
+
export type { AgentStreamEvent, AgentStreamCallbacks, McpState, McpToolResult, McpContentItem, McpServerStatus, McpToolInfo, } from './types.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EACH,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACR,gBAAgB,EAChB,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,GACd,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Module Index
|
|
3
|
+
* Export agent functionality
|
|
4
|
+
*/
|
|
5
|
+
export { AgentService, agentService } from './service.js';
|
|
6
|
+
export { startChat, sendMessage } from './chat.js';
|
|
7
|
+
export { renderMarkdown, renderToolCall, renderToolResult, renderMcpToolCall, renderMcpToolResult, renderError, renderSuccess, renderPrompt, renderAgentLabel, renderWelcome, renderGoodbye, renderThinking, } from './renderer.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EACH,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,GACjB,MAAM,eAAe,CAAC"}
|