pulse-coder-cli 0.0.1-alpha.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/.pulse-coder/agents/code-reviewer.md +36 -0
- package/.pulse-coder/agents/doc-generator.md +37 -0
- package/.pulse-coder/agents/test-writer.md +38 -0
- package/.pulse-coder/mcp.json +8 -0
- package/.pulse-coder/skills/branch-naming/SKILL.md +427 -0
- package/.pulse-coder/skills/code-review/SKILL.md +56 -0
- package/.pulse-coder/skills/deep-research/SKILL.md +124 -0
- package/.pulse-coder/skills/git-workflow/SKILL.md +93 -0
- package/.pulse-coder/skills/mr-generator/README.md +96 -0
- package/.pulse-coder/skills/mr-generator/SKILL.md +113 -0
- package/.pulse-coder/skills/mr-generator/mr-generate.sh +342 -0
- package/.pulse-coder/skills/refactor/SKILL.md +63 -0
- package/README.md +155 -0
- package/dist/index.js +599 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -0
- package/src/index.ts +321 -0
- package/src/input-manager.ts +113 -0
- package/src/session-commands.ts +142 -0
- package/src/session.ts +171 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +12 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { PulseAgent } from "@pulse-coder/engine";
|
|
5
|
+
import * as readline from "readline";
|
|
6
|
+
|
|
7
|
+
// src/session.ts
|
|
8
|
+
import * as fs from "fs/promises";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
var SessionManager = class {
|
|
13
|
+
sessionsDir;
|
|
14
|
+
constructor() {
|
|
15
|
+
this.sessionsDir = path.join(homedir(), ".pulse-coder", "sessions");
|
|
16
|
+
}
|
|
17
|
+
async initialize() {
|
|
18
|
+
try {
|
|
19
|
+
await fs.mkdir(this.sessionsDir, { recursive: true });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async createSession(title) {
|
|
24
|
+
const session = {
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
title: title || `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
27
|
+
createdAt: Date.now(),
|
|
28
|
+
updatedAt: Date.now(),
|
|
29
|
+
messages: [],
|
|
30
|
+
metadata: {
|
|
31
|
+
totalMessages: 0
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
await this.saveSession(session);
|
|
35
|
+
return session;
|
|
36
|
+
}
|
|
37
|
+
async saveSession(session) {
|
|
38
|
+
session.updatedAt = Date.now();
|
|
39
|
+
session.metadata.totalMessages = session.messages.length;
|
|
40
|
+
if (session.messages.length > 0) {
|
|
41
|
+
session.metadata.lastMessageAt = session.messages[session.messages.length - 1].timestamp;
|
|
42
|
+
}
|
|
43
|
+
const filePath = path.join(this.sessionsDir, `${session.id}.json`);
|
|
44
|
+
await fs.writeFile(filePath, JSON.stringify(session, null, 2));
|
|
45
|
+
}
|
|
46
|
+
async loadSession(id) {
|
|
47
|
+
try {
|
|
48
|
+
const filePath = path.join(this.sessionsDir, `${id}.json`);
|
|
49
|
+
const data = await fs.readFile(filePath, "utf-8");
|
|
50
|
+
return JSON.parse(data);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
safeContent(content) {
|
|
56
|
+
if (typeof content === "string") {
|
|
57
|
+
return content;
|
|
58
|
+
}
|
|
59
|
+
if (content === null || content === void 0) {
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
return String(content);
|
|
63
|
+
}
|
|
64
|
+
async listSessions(limit = 20) {
|
|
65
|
+
try {
|
|
66
|
+
const files = await fs.readdir(this.sessionsDir);
|
|
67
|
+
const sessionFiles = files.filter((file) => file.endsWith(".json"));
|
|
68
|
+
const sessions = [];
|
|
69
|
+
for (const file of sessionFiles) {
|
|
70
|
+
const data = await fs.readFile(path.join(this.sessionsDir, file), "utf-8");
|
|
71
|
+
sessions.push(JSON.parse(data));
|
|
72
|
+
}
|
|
73
|
+
return sessions.sort((a, b) => b.updatedAt - a.updatedAt).slice(0, limit).map((session) => ({
|
|
74
|
+
id: session.id,
|
|
75
|
+
title: session.title,
|
|
76
|
+
createdAt: session.createdAt,
|
|
77
|
+
updatedAt: session.updatedAt,
|
|
78
|
+
messageCount: session.messages.length,
|
|
79
|
+
preview: session.messages.length > 0 ? this.safeContent(session.messages[session.messages.length - 1].content).substring(0, 100) + "..." : "No messages"
|
|
80
|
+
}));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async deleteSession(id) {
|
|
86
|
+
try {
|
|
87
|
+
const filePath = path.join(this.sessionsDir, `${id}.json`);
|
|
88
|
+
await fs.unlink(filePath);
|
|
89
|
+
return true;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async updateSessionTitle(id, title) {
|
|
95
|
+
const session = await this.loadSession(id);
|
|
96
|
+
if (!session) return false;
|
|
97
|
+
session.title = title;
|
|
98
|
+
await this.saveSession(session);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
async addMessage(id, message) {
|
|
102
|
+
const session = await this.loadSession(id);
|
|
103
|
+
if (!session) return false;
|
|
104
|
+
session.messages.push({
|
|
105
|
+
...message,
|
|
106
|
+
timestamp: Date.now()
|
|
107
|
+
});
|
|
108
|
+
await this.saveSession(session);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
async searchSessions(query) {
|
|
112
|
+
const sessions = await this.listSessions(100);
|
|
113
|
+
const lowercaseQuery = query.toLowerCase();
|
|
114
|
+
return sessions.filter(
|
|
115
|
+
(session) => session.title.toLowerCase().includes(lowercaseQuery) || session.preview.toLowerCase().includes(lowercaseQuery)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/session-commands.ts
|
|
121
|
+
var SessionCommands = class {
|
|
122
|
+
sessionManager;
|
|
123
|
+
currentSessionId = null;
|
|
124
|
+
constructor() {
|
|
125
|
+
this.sessionManager = new SessionManager();
|
|
126
|
+
}
|
|
127
|
+
async initialize() {
|
|
128
|
+
await this.sessionManager.initialize();
|
|
129
|
+
}
|
|
130
|
+
getCurrentSessionId() {
|
|
131
|
+
return this.currentSessionId;
|
|
132
|
+
}
|
|
133
|
+
async createSession(title) {
|
|
134
|
+
const session = await this.sessionManager.createSession(title);
|
|
135
|
+
this.currentSessionId = session.id;
|
|
136
|
+
console.log(`
|
|
137
|
+
\u2705 New session created: ${session.title} (ID: ${session.id})`);
|
|
138
|
+
return session.id;
|
|
139
|
+
}
|
|
140
|
+
async resumeSession(id) {
|
|
141
|
+
const session = await this.sessionManager.loadSession(id);
|
|
142
|
+
if (!session) {
|
|
143
|
+
console.log(`
|
|
144
|
+
\u274C Session not found: ${id}`);
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
this.currentSessionId = session.id;
|
|
148
|
+
console.log(`
|
|
149
|
+
\u2705 Resumed session: ${session.title} (ID: ${session.id})`);
|
|
150
|
+
console.log(`\u{1F4CA} Loaded ${session.messages.length} messages`);
|
|
151
|
+
const recentMessages = session.messages.slice(-5);
|
|
152
|
+
if (recentMessages.length > 0) {
|
|
153
|
+
console.log("\n\u{1F4AC} Recent conversation:");
|
|
154
|
+
recentMessages.forEach((msg, index) => {
|
|
155
|
+
const role = msg.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant";
|
|
156
|
+
const contentStr = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
157
|
+
const preview = contentStr.substring(0, 100) + (contentStr.length > 100 ? "..." : "");
|
|
158
|
+
console.log(`${index + 1}. ${role}: ${preview}`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
async listSessions() {
|
|
164
|
+
const sessions = await this.sessionManager.listSessions();
|
|
165
|
+
if (sessions.length === 0) {
|
|
166
|
+
console.log("\n\u{1F4ED} No saved sessions found.");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
console.log("\n\u{1F4CB} Saved sessions:");
|
|
170
|
+
console.log("=".repeat(80));
|
|
171
|
+
sessions.forEach((session, index) => {
|
|
172
|
+
const isActive = session.id === this.currentSessionId ? "\u2705" : " ";
|
|
173
|
+
const date = new Date(session.updatedAt).toLocaleString();
|
|
174
|
+
console.log(`${index + 1}. ${isActive} ${session.title}`);
|
|
175
|
+
console.log(` ID: ${session.id}`);
|
|
176
|
+
console.log(` Messages: ${session.messageCount} | Updated: ${date}`);
|
|
177
|
+
console.log(` Preview: ${session.preview}`);
|
|
178
|
+
console.log();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async saveContext(context) {
|
|
182
|
+
if (!this.currentSessionId) return;
|
|
183
|
+
const session = await this.sessionManager.loadSession(this.currentSessionId);
|
|
184
|
+
if (!session) return;
|
|
185
|
+
session.messages = context.messages.map((msg) => ({
|
|
186
|
+
role: msg.role,
|
|
187
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
188
|
+
timestamp: Date.now()
|
|
189
|
+
}));
|
|
190
|
+
await this.sessionManager.saveSession(session);
|
|
191
|
+
}
|
|
192
|
+
async loadContext(context) {
|
|
193
|
+
if (!this.currentSessionId) return;
|
|
194
|
+
const session = await this.sessionManager.loadSession(this.currentSessionId);
|
|
195
|
+
if (!session) return;
|
|
196
|
+
context.messages = session.messages.map((msg) => ({
|
|
197
|
+
role: msg.role,
|
|
198
|
+
content: msg.content
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
async searchSessions(query) {
|
|
202
|
+
const sessions = await this.sessionManager.searchSessions(query);
|
|
203
|
+
if (sessions.length === 0) {
|
|
204
|
+
console.log(`
|
|
205
|
+
\u{1F50D} No sessions found matching "${query}"`);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
console.log(`
|
|
209
|
+
\u{1F50D} Search results for "${query}":`);
|
|
210
|
+
sessions.forEach((session, index) => {
|
|
211
|
+
console.log(`${index + 1}. ${session.title} (${session.id}) - ${session.messageCount} messages`);
|
|
212
|
+
console.log(` Updated: ${new Date(session.updatedAt).toLocaleString()}`);
|
|
213
|
+
console.log(` Preview: ${session.preview}`);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async deleteSession(id) {
|
|
217
|
+
const success = await this.sessionManager.deleteSession(id);
|
|
218
|
+
if (success) {
|
|
219
|
+
console.log(`
|
|
220
|
+
\u{1F5D1}\uFE0F Session ${id} deleted`);
|
|
221
|
+
if (this.currentSessionId === id) {
|
|
222
|
+
this.currentSessionId = null;
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
console.log(`
|
|
226
|
+
\u274C Failed to delete session ${id}`);
|
|
227
|
+
}
|
|
228
|
+
return success;
|
|
229
|
+
}
|
|
230
|
+
async renameSession(id, newTitle) {
|
|
231
|
+
const success = await this.sessionManager.updateSessionTitle(id, newTitle);
|
|
232
|
+
if (success) {
|
|
233
|
+
console.log(`
|
|
234
|
+
\u2705 Session ${id} renamed to "${newTitle}"`);
|
|
235
|
+
} else {
|
|
236
|
+
console.log(`
|
|
237
|
+
\u274C Failed to rename session ${id}`);
|
|
238
|
+
}
|
|
239
|
+
return success;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/input-manager.ts
|
|
244
|
+
var InputManager = class {
|
|
245
|
+
pendingRequest = null;
|
|
246
|
+
/**
|
|
247
|
+
* Request user input for a clarification
|
|
248
|
+
* @param request The clarification request details
|
|
249
|
+
* @returns Promise that resolves with the user's answer
|
|
250
|
+
*/
|
|
251
|
+
async requestInput(request) {
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
if (this.pendingRequest) {
|
|
254
|
+
reject(new Error("Another clarification request is already pending"));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
console.log(`
|
|
258
|
+
\u2753 ${request.question}`);
|
|
259
|
+
if (request.context) {
|
|
260
|
+
console.log(` ${request.context}`);
|
|
261
|
+
}
|
|
262
|
+
if (request.defaultAnswer) {
|
|
263
|
+
console.log(` (Default: ${request.defaultAnswer})`);
|
|
264
|
+
}
|
|
265
|
+
console.log("");
|
|
266
|
+
let timeoutId;
|
|
267
|
+
if (request.timeout > 0) {
|
|
268
|
+
timeoutId = setTimeout(() => {
|
|
269
|
+
if (this.pendingRequest?.request.id === request.id) {
|
|
270
|
+
const error = new Error(`Clarification request timed out after ${request.timeout}ms`);
|
|
271
|
+
this.pendingRequest.reject(error);
|
|
272
|
+
this.pendingRequest = null;
|
|
273
|
+
}
|
|
274
|
+
}, request.timeout);
|
|
275
|
+
}
|
|
276
|
+
this.pendingRequest = {
|
|
277
|
+
request,
|
|
278
|
+
resolve,
|
|
279
|
+
reject,
|
|
280
|
+
timeoutId
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Handle user input - checks if there's a pending clarification request
|
|
286
|
+
* @param input The user's input
|
|
287
|
+
* @returns true if input was consumed by a pending clarification, false otherwise
|
|
288
|
+
*/
|
|
289
|
+
handleUserInput(input) {
|
|
290
|
+
if (!this.pendingRequest) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (this.pendingRequest.timeoutId) {
|
|
294
|
+
clearTimeout(this.pendingRequest.timeoutId);
|
|
295
|
+
}
|
|
296
|
+
const trimmedInput = input.trim();
|
|
297
|
+
this.pendingRequest.resolve(trimmedInput);
|
|
298
|
+
this.pendingRequest = null;
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Cancel any pending clarification request
|
|
303
|
+
* @param reason Optional cancellation reason
|
|
304
|
+
*/
|
|
305
|
+
cancel(reason) {
|
|
306
|
+
if (this.pendingRequest) {
|
|
307
|
+
if (this.pendingRequest.timeoutId) {
|
|
308
|
+
clearTimeout(this.pendingRequest.timeoutId);
|
|
309
|
+
}
|
|
310
|
+
this.pendingRequest.reject(new Error(reason || "Clarification request cancelled"));
|
|
311
|
+
this.pendingRequest = null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Check if there's a pending clarification request
|
|
316
|
+
*/
|
|
317
|
+
hasPendingRequest() {
|
|
318
|
+
return this.pendingRequest !== null;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get the current pending request (for debugging/testing)
|
|
322
|
+
*/
|
|
323
|
+
getPendingRequest() {
|
|
324
|
+
return this.pendingRequest?.request || null;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/index.ts
|
|
329
|
+
var CoderCLI = class {
|
|
330
|
+
agent;
|
|
331
|
+
context;
|
|
332
|
+
sessionCommands;
|
|
333
|
+
inputManager;
|
|
334
|
+
constructor() {
|
|
335
|
+
this.agent = new PulseAgent({
|
|
336
|
+
enginePlugins: {
|
|
337
|
+
// ๅช้
็ฝฎๆฉๅฑๆไปถ็ฎๅฝ๏ผๅ
็ฝฎๆไปถไผ่ชๅจๅ ่ฝฝ
|
|
338
|
+
dirs: [".pulse-coder/engine-plugins", ".coder/engine-plugins", "~/.pulse-coder/engine-plugins", "~/.coder/engine-plugins"],
|
|
339
|
+
scan: true
|
|
340
|
+
},
|
|
341
|
+
userConfigPlugins: {
|
|
342
|
+
dirs: [".pulse-coder/config", ".coder/config", "~/.pulse-coder/config", "~/.coder/config"],
|
|
343
|
+
scan: true
|
|
344
|
+
}
|
|
345
|
+
// ๆณจๆ๏ผไธๅ้่ฆ plugins: [...] ้
็ฝฎ
|
|
346
|
+
});
|
|
347
|
+
this.context = { messages: [] };
|
|
348
|
+
this.sessionCommands = new SessionCommands();
|
|
349
|
+
this.inputManager = new InputManager();
|
|
350
|
+
}
|
|
351
|
+
async handleCommand(command, args) {
|
|
352
|
+
try {
|
|
353
|
+
switch (command.toLowerCase()) {
|
|
354
|
+
case "help":
|
|
355
|
+
console.log("\n\u{1F4CB} Available commands:");
|
|
356
|
+
console.log("/help - Show this help message");
|
|
357
|
+
console.log("/new [title] - Create a new session");
|
|
358
|
+
console.log("/resume <id> - Resume a saved session");
|
|
359
|
+
console.log("/sessions - List all saved sessions");
|
|
360
|
+
console.log("/search <query> - Search in saved sessions");
|
|
361
|
+
console.log("/rename <id> <new-title> - Rename a session");
|
|
362
|
+
console.log("/delete <id> - Delete a session");
|
|
363
|
+
console.log("/clear - Clear current conversation");
|
|
364
|
+
console.log("/status - Show current session status");
|
|
365
|
+
console.log("/save - Save current session explicitly");
|
|
366
|
+
console.log("/exit - Exit the application");
|
|
367
|
+
break;
|
|
368
|
+
case "new":
|
|
369
|
+
const newTitle = args.join(" ") || void 0;
|
|
370
|
+
await this.sessionCommands.createSession(newTitle);
|
|
371
|
+
this.context.messages = [];
|
|
372
|
+
break;
|
|
373
|
+
case "resume":
|
|
374
|
+
if (args.length === 0) {
|
|
375
|
+
console.log("\n\u274C Please provide a session ID");
|
|
376
|
+
console.log("Usage: /resume <session-id>");
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
const sessionId = args[0];
|
|
380
|
+
const success = await this.sessionCommands.resumeSession(sessionId);
|
|
381
|
+
if (success) {
|
|
382
|
+
await this.sessionCommands.loadContext(this.context);
|
|
383
|
+
}
|
|
384
|
+
break;
|
|
385
|
+
case "sessions":
|
|
386
|
+
await this.sessionCommands.listSessions();
|
|
387
|
+
break;
|
|
388
|
+
case "search":
|
|
389
|
+
if (args.length === 0) {
|
|
390
|
+
console.log("\n\u274C Please provide a search query");
|
|
391
|
+
console.log("Usage: /search <query>");
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
const query = args.join(" ");
|
|
395
|
+
await this.sessionCommands.searchSessions(query);
|
|
396
|
+
break;
|
|
397
|
+
case "rename":
|
|
398
|
+
if (args.length < 2) {
|
|
399
|
+
console.log("\n\u274C Please provide session ID and new title");
|
|
400
|
+
console.log("Usage: /rename <session-id> <new-title>");
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
const renameId = args[0];
|
|
404
|
+
const newName = args.slice(1).join(" ");
|
|
405
|
+
await this.sessionCommands.renameSession(renameId, newName);
|
|
406
|
+
break;
|
|
407
|
+
case "delete":
|
|
408
|
+
if (args.length === 0) {
|
|
409
|
+
console.log("\n\u274C Please provide a session ID");
|
|
410
|
+
console.log("Usage: /delete <session-id>");
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
const deleteId = args[0];
|
|
414
|
+
await this.sessionCommands.deleteSession(deleteId);
|
|
415
|
+
break;
|
|
416
|
+
case "clear":
|
|
417
|
+
this.context.messages = [];
|
|
418
|
+
console.log("\n\u{1F9F9} Current conversation cleared!");
|
|
419
|
+
break;
|
|
420
|
+
case "status":
|
|
421
|
+
const currentId = this.sessionCommands.getCurrentSessionId();
|
|
422
|
+
console.log(`
|
|
423
|
+
\u{1F4CA} Session Status:`);
|
|
424
|
+
console.log(`Current Session: ${currentId || "None (new session)"}`);
|
|
425
|
+
console.log(`Messages: ${this.context.messages.length}`);
|
|
426
|
+
if (currentId) {
|
|
427
|
+
console.log(`To save this session, use: /save`);
|
|
428
|
+
}
|
|
429
|
+
break;
|
|
430
|
+
case "save":
|
|
431
|
+
if (this.sessionCommands.getCurrentSessionId()) {
|
|
432
|
+
await this.sessionCommands.saveContext(this.context);
|
|
433
|
+
console.log("\n\u{1F4BE} Current session saved!");
|
|
434
|
+
} else {
|
|
435
|
+
console.log("\n\u274C No active session. Create one with /new");
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
case "exit":
|
|
439
|
+
console.log("\u{1F4BE} Saving current session...");
|
|
440
|
+
await this.sessionCommands.saveContext(this.context);
|
|
441
|
+
console.log("Goodbye!");
|
|
442
|
+
process.exit(0);
|
|
443
|
+
break;
|
|
444
|
+
default:
|
|
445
|
+
console.log(`
|
|
446
|
+
\u26A0\uFE0F Unknown command: ${command}`);
|
|
447
|
+
console.log("Type /help to see available commands");
|
|
448
|
+
}
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error("\n\u274C Error executing command:", error);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
async start() {
|
|
454
|
+
console.log("\u{1F680} Pulse Coder CLI is running...");
|
|
455
|
+
console.log('Type your messages and press Enter. Type "exit" to quit.');
|
|
456
|
+
console.log('Commands starting with "/" will trigger command mode.\n');
|
|
457
|
+
await this.sessionCommands.initialize();
|
|
458
|
+
await this.agent.initialize();
|
|
459
|
+
const pluginStatus = this.agent.getPluginStatus();
|
|
460
|
+
console.log(`\u2705 Built-in plugins loaded: ${pluginStatus.enginePlugins.length} plugins`);
|
|
461
|
+
await this.sessionCommands.createSession();
|
|
462
|
+
const rl = readline.createInterface({
|
|
463
|
+
input: process.stdin,
|
|
464
|
+
output: process.stdout,
|
|
465
|
+
prompt: "> "
|
|
466
|
+
});
|
|
467
|
+
let currentAbortController = null;
|
|
468
|
+
let isProcessing = false;
|
|
469
|
+
process.on("SIGINT", () => {
|
|
470
|
+
if (isProcessing && currentAbortController && !currentAbortController.signal.aborted) {
|
|
471
|
+
currentAbortController.abort();
|
|
472
|
+
console.log("\n[Abort] Request cancelled.");
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (this.inputManager.hasPendingRequest()) {
|
|
476
|
+
this.inputManager.cancel("User interrupted with Ctrl+C");
|
|
477
|
+
console.log("\n[Abort] Clarification cancelled.");
|
|
478
|
+
rl.prompt();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
console.log("\n\u{1F4BE} Saving current session...");
|
|
482
|
+
this.sessionCommands.saveContext(this.context).then(() => {
|
|
483
|
+
console.log("\u{1F44B} Goodbye!");
|
|
484
|
+
process.exit(0);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
const handleInput = async (input) => {
|
|
488
|
+
const trimmedInput = input.trim();
|
|
489
|
+
if (this.inputManager.handleUserInput(trimmedInput)) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (trimmedInput.toLowerCase() === "exit") {
|
|
493
|
+
console.log("\u{1F4BE} Saving current session...");
|
|
494
|
+
await this.sessionCommands.saveContext(this.context);
|
|
495
|
+
console.log("\u{1F44B} Goodbye!");
|
|
496
|
+
rl.close();
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (!trimmedInput) {
|
|
500
|
+
rl.prompt();
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (trimmedInput.startsWith("/")) {
|
|
504
|
+
const commandLine = trimmedInput.substring(1);
|
|
505
|
+
const parts = commandLine.split(/\s+/).filter((part) => part.length > 0);
|
|
506
|
+
if (parts.length === 0) {
|
|
507
|
+
console.log('\n\u26A0\uFE0F Please provide a command after "/"');
|
|
508
|
+
rl.prompt();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const command = parts[0];
|
|
512
|
+
const args = parts.slice(1);
|
|
513
|
+
await this.handleCommand(command, args);
|
|
514
|
+
rl.prompt();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
this.context.messages.push({
|
|
518
|
+
role: "user",
|
|
519
|
+
content: trimmedInput
|
|
520
|
+
});
|
|
521
|
+
console.log("\n\u{1F504} Processing...\n");
|
|
522
|
+
const ac = new AbortController();
|
|
523
|
+
currentAbortController = ac;
|
|
524
|
+
isProcessing = true;
|
|
525
|
+
let sawText = false;
|
|
526
|
+
try {
|
|
527
|
+
const result = await this.agent.run(this.context, {
|
|
528
|
+
abortSignal: ac.signal,
|
|
529
|
+
onText: (delta) => {
|
|
530
|
+
sawText = true;
|
|
531
|
+
process.stdout.write(delta);
|
|
532
|
+
},
|
|
533
|
+
onToolCall: (toolCall) => {
|
|
534
|
+
const input2 = "input" in toolCall ? toolCall.input : void 0;
|
|
535
|
+
const inputText = input2 === void 0 ? "" : `(${JSON.stringify(input2)})`;
|
|
536
|
+
process.stdout.write(`
|
|
537
|
+
\u{1F527} ${toolCall.toolName}${inputText}
|
|
538
|
+
`);
|
|
539
|
+
},
|
|
540
|
+
onToolResult: (toolResult) => {
|
|
541
|
+
process.stdout.write(`
|
|
542
|
+
\u2705 ${toolResult.toolName}
|
|
543
|
+
`);
|
|
544
|
+
},
|
|
545
|
+
onStepFinish: (step) => {
|
|
546
|
+
process.stdout.write(`
|
|
547
|
+
\u{1F4CB} Step finished: ${step.finishReason}
|
|
548
|
+
`);
|
|
549
|
+
},
|
|
550
|
+
onClarificationRequest: async (request) => {
|
|
551
|
+
return await this.inputManager.requestInput(request);
|
|
552
|
+
},
|
|
553
|
+
onCompacted: (newMessages) => {
|
|
554
|
+
this.context.messages = newMessages;
|
|
555
|
+
},
|
|
556
|
+
onResponse: (messages) => {
|
|
557
|
+
this.context.messages.push(...messages);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
if (result) {
|
|
561
|
+
if (!sawText) {
|
|
562
|
+
console.log(result);
|
|
563
|
+
} else {
|
|
564
|
+
console.log();
|
|
565
|
+
}
|
|
566
|
+
this.context.messages.push({
|
|
567
|
+
role: "assistant",
|
|
568
|
+
content: result
|
|
569
|
+
});
|
|
570
|
+
await this.sessionCommands.saveContext(this.context);
|
|
571
|
+
}
|
|
572
|
+
} catch (error) {
|
|
573
|
+
if (error.name === "AbortError") {
|
|
574
|
+
console.log("\n[Abort] Operation cancelled.");
|
|
575
|
+
} else {
|
|
576
|
+
console.error("\n\u274C Error:", error.message);
|
|
577
|
+
}
|
|
578
|
+
} finally {
|
|
579
|
+
isProcessing = false;
|
|
580
|
+
currentAbortController = null;
|
|
581
|
+
rl.prompt();
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
rl.prompt();
|
|
585
|
+
rl.on("line", handleInput);
|
|
586
|
+
rl.on("close", async () => {
|
|
587
|
+
console.log("\n\u{1F4BE} Saving current session...");
|
|
588
|
+
await this.sessionCommands.saveContext(this.context);
|
|
589
|
+
console.log("\u{1F44B} Goodbye!");
|
|
590
|
+
process.exit(0);
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
var cli = new CoderCLI();
|
|
595
|
+
cli.start().catch((error) => {
|
|
596
|
+
console.error("Failed to start CLI:", error);
|
|
597
|
+
process.exit(1);
|
|
598
|
+
});
|
|
599
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/session.ts","../src/session-commands.ts","../src/input-manager.ts"],"sourcesContent":["import { PulseAgent } from '@pulse-coder/engine';\nimport * as readline from 'readline';\nimport type { Context } from '@pulse-coder/engine';\nimport { SessionCommands } from './session-commands.js';\nimport { InputManager } from './input-manager.js';\n\nclass CoderCLI {\n private agent: PulseAgent;\n private context: Context;\n private sessionCommands: SessionCommands;\n private inputManager: InputManager;\n\n constructor() {\n // ๐ฏ ็ฐๅจๅผๆ่ชๅจๅ
ๅซๅ
็ฝฎๆไปถ๏ผๆ ้ๆพๅผ้
็ฝฎ๏ผ\n this.agent = new PulseAgent({\n enginePlugins: {\n // ๅช้
็ฝฎๆฉๅฑๆไปถ็ฎๅฝ๏ผๅ
็ฝฎๆไปถไผ่ชๅจๅ ่ฝฝ\n dirs: ['.pulse-coder/engine-plugins', '.coder/engine-plugins', '~/.pulse-coder/engine-plugins', '~/.coder/engine-plugins'],\n scan: true\n },\n userConfigPlugins: {\n dirs: ['.pulse-coder/config', '.coder/config', '~/.pulse-coder/config', '~/.coder/config'],\n scan: true\n }\n // ๆณจๆ๏ผไธๅ้่ฆ plugins: [...] ้
็ฝฎ\n });\n this.context = { messages: [] };\n this.sessionCommands = new SessionCommands();\n this.inputManager = new InputManager();\n }\n\n private async handleCommand(command: string, args: string[]): Promise<void> {\n try {\n switch (command.toLowerCase()) {\n case 'help':\n console.log('\\n๐ Available commands:');\n console.log('/help - Show this help message');\n console.log('/new [title] - Create a new session');\n console.log('/resume <id> - Resume a saved session');\n console.log('/sessions - List all saved sessions');\n console.log('/search <query> - Search in saved sessions');\n console.log('/rename <id> <new-title> - Rename a session');\n console.log('/delete <id> - Delete a session');\n console.log('/clear - Clear current conversation');\n console.log('/status - Show current session status');\n console.log('/save - Save current session explicitly');\n console.log('/exit - Exit the application');\n break;\n\n case 'new':\n const newTitle = args.join(' ') || undefined;\n await this.sessionCommands.createSession(newTitle);\n this.context.messages = [];\n break;\n\n case 'resume':\n if (args.length === 0) {\n console.log('\\nโ Please provide a session ID');\n console.log('Usage: /resume <session-id>');\n break;\n }\n const sessionId = args[0];\n const success = await this.sessionCommands.resumeSession(sessionId);\n if (success) {\n await this.sessionCommands.loadContext(this.context);\n }\n break;\n\n case 'sessions':\n await this.sessionCommands.listSessions();\n break;\n\n case 'search':\n if (args.length === 0) {\n console.log('\\nโ Please provide a search query');\n console.log('Usage: /search <query>');\n break;\n }\n const query = args.join(' ');\n await this.sessionCommands.searchSessions(query);\n break;\n\n case 'rename':\n if (args.length < 2) {\n console.log('\\nโ Please provide session ID and new title');\n console.log('Usage: /rename <session-id> <new-title>');\n break;\n }\n const renameId = args[0];\n const newName = args.slice(1).join(' ');\n await this.sessionCommands.renameSession(renameId, newName);\n break;\n\n case 'delete':\n if (args.length === 0) {\n console.log('\\nโ Please provide a session ID');\n console.log('Usage: /delete <session-id>');\n break;\n }\n const deleteId = args[0];\n await this.sessionCommands.deleteSession(deleteId);\n break;\n\n case 'clear':\n this.context.messages = [];\n console.log('\\n๐งน Current conversation cleared!');\n break;\n\n case 'status':\n const currentId = this.sessionCommands.getCurrentSessionId();\n console.log(`\\n๐ Session Status:`);\n console.log(`Current Session: ${currentId || 'None (new session)'}`);\n console.log(`Messages: ${this.context.messages.length}`);\n if (currentId) {\n console.log(`To save this session, use: /save`);\n }\n break;\n\n case 'save':\n if (this.sessionCommands.getCurrentSessionId()) {\n await this.sessionCommands.saveContext(this.context);\n console.log('\\n๐พ Current session saved!');\n } else {\n console.log('\\nโ No active session. Create one with /new');\n }\n break;\n\n case 'exit':\n console.log('๐พ Saving current session...');\n await this.sessionCommands.saveContext(this.context);\n console.log('Goodbye!');\n process.exit(0);\n break;\n\n default:\n console.log(`\\nโ ๏ธ Unknown command: ${command}`);\n console.log('Type /help to see available commands');\n }\n } catch (error) {\n console.error('\\nโ Error executing command:', error);\n }\n }\n\n async start() {\n console.log('๐ Pulse Coder CLI is running...');\n console.log('Type your messages and press Enter. Type \"exit\" to quit.');\n console.log('Commands starting with \"/\" will trigger command mode.\\n');\n\n await this.sessionCommands.initialize();\n await this.agent.initialize();\n\n // ๆพ็คบๆไปถ็ถๆ\n const pluginStatus = this.agent.getPluginStatus();\n console.log(`โ
Built-in plugins loaded: ${pluginStatus.enginePlugins.length} plugins`);\n\n // Auto-create a new session\n await this.sessionCommands.createSession();\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: '> '\n });\n\n let currentAbortController: AbortController | null = null;\n let isProcessing = false;\n\n // Handle SIGINT gracefully\n process.on('SIGINT', () => {\n if (isProcessing && currentAbortController && !currentAbortController.signal.aborted) {\n currentAbortController.abort();\n console.log('\\n[Abort] Request cancelled.');\n return;\n }\n\n // Cancel any pending clarification request\n if (this.inputManager.hasPendingRequest()) {\n this.inputManager.cancel('User interrupted with Ctrl+C');\n console.log('\\n[Abort] Clarification cancelled.');\n rl.prompt();\n return;\n }\n\n console.log('\\n๐พ Saving current session...');\n this.sessionCommands.saveContext(this.context).then(() => {\n console.log('๐ Goodbye!');\n process.exit(0);\n });\n });\n\n // Main input handler\n const handleInput = async (input: string) => {\n const trimmedInput = input.trim();\n\n // Handle clarification requests first\n if (this.inputManager.handleUserInput(trimmedInput)) {\n return;\n }\n\n if (trimmedInput.toLowerCase() === 'exit') {\n console.log('๐พ Saving current session...');\n await this.sessionCommands.saveContext(this.context);\n console.log('๐ Goodbye!');\n rl.close();\n return;\n }\n\n if (!trimmedInput) {\n rl.prompt();\n return;\n }\n\n // Handle commands\n if (trimmedInput.startsWith('/')) {\n const commandLine = trimmedInput.substring(1);\n const parts = commandLine.split(/\\s+/).filter(part => part.length > 0);\n\n if (parts.length === 0) {\n console.log('\\nโ ๏ธ Please provide a command after \"/\"');\n rl.prompt();\n return;\n }\n\n const command = parts[0];\n const args = parts.slice(1);\n\n await this.handleCommand(command, args);\n rl.prompt();\n return;\n }\n\n // Regular message processing\n this.context.messages.push({\n role: 'user',\n content: trimmedInput,\n });\n\n console.log('\\n๐ Processing...\\n');\n\n const ac = new AbortController();\n currentAbortController = ac;\n isProcessing = true;\n\n let sawText = false;\n\n try {\n const result = await this.agent.run(this.context, {\n abortSignal: ac.signal,\n onText: (delta) => {\n sawText = true;\n process.stdout.write(delta);\n },\n onToolCall: (toolCall) => {\n const input = 'input' in toolCall ? toolCall.input : undefined;\n const inputText = input === undefined ? '' : `(${JSON.stringify(input)})`;\n process.stdout.write(`\\n๐ง ${toolCall.toolName}${inputText}\\n`);\n },\n onToolResult: (toolResult) => {\n process.stdout.write(`\\nโ
${toolResult.toolName}\\n`);\n },\n onStepFinish: (step) => {\n process.stdout.write(`\\n๐ Step finished: ${step.finishReason}\\n`);\n },\n onClarificationRequest: async (request) => {\n return await this.inputManager.requestInput(request);\n },\n onCompacted: (newMessages) => {\n this.context.messages = newMessages;\n },\n onResponse: (messages) => {\n this.context.messages.push(...messages);\n },\n });\n\n if (result) {\n if (!sawText) {\n console.log(result);\n } else {\n console.log();\n }\n\n this.context.messages.push({\n role: 'assistant',\n content: result,\n });\n\n await this.sessionCommands.saveContext(this.context);\n }\n } catch (error) {\n if (error.name === 'AbortError') {\n console.log('\\n[Abort] Operation cancelled.');\n } else {\n console.error('\\nโ Error:', error.message);\n }\n } finally {\n isProcessing = false;\n currentAbortController = null;\n rl.prompt();\n }\n };\n\n // Start the CLI\n rl.prompt();\n rl.on('line', handleInput);\n\n // Handle terminal close\n rl.on('close', async () => {\n console.log('\\n๐พ Saving current session...');\n await this.sessionCommands.saveContext(this.context);\n console.log('๐ Goodbye!');\n process.exit(0);\n });\n }\n}\n\n// Always start the CLI when executed directly\nconst cli = new CoderCLI();\ncli.start().catch(error => {\n console.error('Failed to start CLI:', error);\n process.exit(1);\n});","import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { homedir } from 'os';\nimport { randomUUID } from 'crypto';\n\nexport interface SessionMessage {\n role: 'user' | 'assistant' | 'system' | 'clarification';\n content: string;\n timestamp: number;\n metadata?: {\n clarificationType?: 'question' | 'answer';\n clarificationId?: string;\n [key: string]: any;\n };\n}\n\nexport interface Session {\n id: string;\n title: string;\n createdAt: number;\n updatedAt: number;\n messages: SessionMessage[];\n metadata: {\n totalMessages: number;\n lastMessageAt?: number;\n tags?: string[];\n };\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n createdAt: number;\n updatedAt: number;\n messageCount: number;\n preview: string;\n}\n\nexport class SessionManager {\n private sessionsDir: string;\n\n constructor() {\n this.sessionsDir = path.join(homedir(), '.pulse-coder', 'sessions');\n }\n\n async initialize(): Promise<void> {\n try {\n await fs.mkdir(this.sessionsDir, { recursive: true });\n } catch (error) {\n // Directory might already exist\n }\n }\n\n async createSession(title?: string): Promise<Session> {\n const session: Session = {\n id: randomUUID(),\n title: title || `Session ${new Date().toLocaleString()}`,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n messages: [],\n metadata: {\n totalMessages: 0,\n },\n };\n\n await this.saveSession(session);\n return session;\n }\n\n async saveSession(session: Session): Promise<void> {\n session.updatedAt = Date.now();\n session.metadata.totalMessages = session.messages.length;\n if (session.messages.length > 0) {\n session.metadata.lastMessageAt = session.messages[session.messages.length - 1].timestamp;\n }\n\n const filePath = path.join(this.sessionsDir, `${session.id}.json`);\n await fs.writeFile(filePath, JSON.stringify(session, null, 2));\n }\n\n async loadSession(id: string): Promise<Session | null> {\n try {\n const filePath = path.join(this.sessionsDir, `${id}.json`);\n const data = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(data);\n } catch (error) {\n return null;\n }\n }\n\n private safeContent(content: any): string {\n if (typeof content === 'string') {\n return content;\n }\n if (content === null || content === undefined) {\n return '';\n }\n return String(content);\n }\n\n async listSessions(limit = 20): Promise<SessionSummary[]> {\n try {\n const files = await fs.readdir(this.sessionsDir);\n const sessionFiles = files.filter(file => file.endsWith('.json'));\n \n const sessions: Session[] = [];\n for (const file of sessionFiles) {\n const data = await fs.readFile(path.join(this.sessionsDir, file), 'utf-8');\n sessions.push(JSON.parse(data));\n }\n\n return sessions\n .sort((a, b) => b.updatedAt - a.updatedAt)\n .slice(0, limit)\n .map(session => ({\n id: session.id,\n title: session.title,\n createdAt: session.createdAt,\n updatedAt: session.updatedAt,\n messageCount: session.messages.length,\n preview: session.messages.length > 0 \n ? this.safeContent(session.messages[session.messages.length - 1].content).substring(0, 100) + '...'\n : 'No messages',\n }));\n } catch (error) {\n return [];\n }\n }\n\n async deleteSession(id: string): Promise<boolean> {\n try {\n const filePath = path.join(this.sessionsDir, `${id}.json`);\n await fs.unlink(filePath);\n return true;\n } catch (error) {\n return false;\n }\n }\n\n async updateSessionTitle(id: string, title: string): Promise<boolean> {\n const session = await this.loadSession(id);\n if (!session) return false;\n\n session.title = title;\n await this.saveSession(session);\n return true;\n }\n\n async addMessage(id: string, message: Omit<SessionMessage, 'timestamp'>): Promise<boolean> {\n const session = await this.loadSession(id);\n if (!session) return false;\n\n session.messages.push({\n ...message,\n timestamp: Date.now(),\n });\n\n await this.saveSession(session);\n return true;\n }\n\n async searchSessions(query: string): Promise<SessionSummary[]> {\n const sessions = await this.listSessions(100); // Get more for search\n const lowercaseQuery = query.toLowerCase();\n \n return sessions.filter(session =>\n session.title.toLowerCase().includes(lowercaseQuery) ||\n session.preview.toLowerCase().includes(lowercaseQuery)\n );\n }\n}","import { SessionManager } from './session.js';\nimport type { Context } from '@pulse-coder/engine';\n\nexport class SessionCommands {\n private sessionManager: SessionManager;\n private currentSessionId: string | null = null;\n\n constructor() {\n this.sessionManager = new SessionManager();\n }\n\n async initialize(): Promise<void> {\n await this.sessionManager.initialize();\n }\n\n getCurrentSessionId(): string | null {\n return this.currentSessionId;\n }\n\n async createSession(title?: string): Promise<string> {\n const session = await this.sessionManager.createSession(title);\n this.currentSessionId = session.id;\n console.log(`\\nโ
New session created: ${session.title} (ID: ${session.id})`);\n return session.id;\n }\n\n async resumeSession(id: string): Promise<boolean> {\n const session = await this.sessionManager.loadSession(id);\n if (!session) {\n console.log(`\\nโ Session not found: ${id}`);\n return false;\n }\n\n this.currentSessionId = session.id;\n console.log(`\\nโ
Resumed session: ${session.title} (ID: ${session.id})`);\n console.log(`๐ Loaded ${session.messages.length} messages`);\n\n // Show last few messages as context\n const recentMessages = session.messages.slice(-5);\n if (recentMessages.length > 0) {\n console.log('\\n๐ฌ Recent conversation:');\n recentMessages.forEach((msg, index) => {\n const role = msg.role === 'user' ? '๐ค You' : '๐ค Assistant';\n const contentStr = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n const preview = contentStr.substring(0, 100) + (contentStr.length > 100 ? '...' : '');\n console.log(`${index + 1}. ${role}: ${preview}`);\n });\n }\n\n return true;\n }\n\n async listSessions(): Promise<void> {\n const sessions = await this.sessionManager.listSessions();\n \n if (sessions.length === 0) {\n console.log('\\n๐ญ No saved sessions found.');\n return;\n }\n\n console.log('\\n๐ Saved sessions:');\n console.log('='.repeat(80));\n \n sessions.forEach((session, index) => {\n const isActive = session.id === this.currentSessionId ? 'โ
' : ' ';\n const date = new Date(session.updatedAt).toLocaleString();\n console.log(`${index + 1}. ${isActive} ${session.title}`);\n console.log(` ID: ${session.id}`);\n console.log(` Messages: ${session.messageCount} | Updated: ${date}`);\n console.log(` Preview: ${session.preview}`);\n console.log();\n });\n }\n\n async saveContext(context: Context): Promise<void> {\n if (!this.currentSessionId) return;\n\n const session = await this.sessionManager.loadSession(this.currentSessionId);\n if (!session) return;\n\n // Sync messages from context\n session.messages = context.messages.map(msg => ({\n role: msg.role as 'user' | 'assistant' | 'system',\n content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),\n timestamp: Date.now(),\n }));\n\n await this.sessionManager.saveSession(session);\n }\n\n async loadContext(context: Context): Promise<void> {\n if (!this.currentSessionId) return;\n\n const session = await this.sessionManager.loadSession(this.currentSessionId);\n if (!session) return;\n\n // Load messages into context\n context.messages = session.messages.map(msg => ({\n role: msg.role,\n content: msg.content,\n }));\n }\n\n async searchSessions(query: string): Promise<void> {\n const sessions = await this.sessionManager.searchSessions(query);\n \n if (sessions.length === 0) {\n console.log(`\\n๐ No sessions found matching \"${query}\"`);\n return;\n }\n\n console.log(`\\n๐ Search results for \"${query}\":`);\n sessions.forEach((session, index) => {\n console.log(`${index + 1}. ${session.title} (${session.id}) - ${session.messageCount} messages`);\n console.log(` Updated: ${new Date(session.updatedAt).toLocaleString()}`);\n console.log(` Preview: ${session.preview}`);\n });\n }\n\n async deleteSession(id: string): Promise<boolean> {\n const success = await this.sessionManager.deleteSession(id);\n if (success) {\n console.log(`\\n๐๏ธ Session ${id} deleted`);\n if (this.currentSessionId === id) {\n this.currentSessionId = null;\n }\n } else {\n console.log(`\\nโ Failed to delete session ${id}`);\n }\n return success;\n }\n\n async renameSession(id: string, newTitle: string): Promise<boolean> {\n const success = await this.sessionManager.updateSessionTitle(id, newTitle);\n if (success) {\n console.log(`\\nโ
Session ${id} renamed to \"${newTitle}\"`);\n } else {\n console.log(`\\nโ Failed to rename session ${id}`);\n }\n return success;\n }\n}","import type { ClarificationRequest } from '@pulse-coder/engine';\n\ninterface PendingRequest {\n request: ClarificationRequest;\n resolve: (answer: string) => void;\n reject: (error: Error) => void;\n timeoutId?: NodeJS.Timeout;\n}\n\n/**\n * InputManager handles asynchronous user input for clarification requests.\n * It manages pending clarification requests and coordinates with the readline interface.\n */\nexport class InputManager {\n private pendingRequest: PendingRequest | null = null;\n\n /**\n * Request user input for a clarification\n * @param request The clarification request details\n * @returns Promise that resolves with the user's answer\n */\n async requestInput(request: ClarificationRequest): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n // If there's already a pending request, reject this one\n if (this.pendingRequest) {\n reject(new Error('Another clarification request is already pending'));\n return;\n }\n\n // Display the question to the user\n console.log(`\\nโ ${request.question}`);\n if (request.context) {\n console.log(` ${request.context}`);\n }\n if (request.defaultAnswer) {\n console.log(` (Default: ${request.defaultAnswer})`);\n }\n console.log(''); // Empty line for spacing\n\n // Set up timeout if specified\n let timeoutId: NodeJS.Timeout | undefined;\n if (request.timeout > 0) {\n timeoutId = setTimeout(() => {\n if (this.pendingRequest?.request.id === request.id) {\n const error = new Error(`Clarification request timed out after ${request.timeout}ms`);\n this.pendingRequest.reject(error);\n this.pendingRequest = null;\n }\n }, request.timeout);\n }\n\n // Store the pending request\n this.pendingRequest = {\n request,\n resolve,\n reject,\n timeoutId\n };\n });\n }\n\n /**\n * Handle user input - checks if there's a pending clarification request\n * @param input The user's input\n * @returns true if input was consumed by a pending clarification, false otherwise\n */\n handleUserInput(input: string): boolean {\n if (!this.pendingRequest) {\n return false;\n }\n\n // Clear the timeout if it exists\n if (this.pendingRequest.timeoutId) {\n clearTimeout(this.pendingRequest.timeoutId);\n }\n\n // Resolve the pending request with the user's input\n const trimmedInput = input.trim();\n this.pendingRequest.resolve(trimmedInput);\n this.pendingRequest = null;\n\n return true;\n }\n\n /**\n * Cancel any pending clarification request\n * @param reason Optional cancellation reason\n */\n cancel(reason?: string): void {\n if (this.pendingRequest) {\n if (this.pendingRequest.timeoutId) {\n clearTimeout(this.pendingRequest.timeoutId);\n }\n\n this.pendingRequest.reject(new Error(reason || 'Clarification request cancelled'));\n this.pendingRequest = null;\n }\n }\n\n /**\n * Check if there's a pending clarification request\n */\n hasPendingRequest(): boolean {\n return this.pendingRequest !== null;\n }\n\n /**\n * Get the current pending request (for debugging/testing)\n */\n getPendingRequest(): ClarificationRequest | null {\n return this.pendingRequest?.request || null;\n }\n}\n"],"mappings":"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","names":["input"]}
|