@simonfestl/husky-cli 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -116
- package/dist/commands/biz/customers.d.ts +8 -0
- package/dist/commands/biz/customers.js +181 -0
- package/dist/commands/biz/orders.d.ts +8 -0
- package/dist/commands/biz/orders.js +226 -0
- package/dist/commands/biz/products.d.ts +8 -0
- package/dist/commands/biz/products.js +255 -0
- package/dist/commands/biz/qdrant.d.ts +8 -0
- package/dist/commands/biz/qdrant.js +170 -0
- package/dist/commands/biz/seatable.d.ts +8 -0
- package/dist/commands/biz/seatable.js +449 -0
- package/dist/commands/biz/tickets.d.ts +8 -0
- package/dist/commands/biz/tickets.js +600 -0
- package/dist/commands/biz.d.ts +9 -0
- package/dist/commands/biz.js +22 -0
- package/dist/commands/config.d.ts +13 -0
- package/dist/commands/config.js +43 -16
- package/dist/commands/explain.js +12 -595
- package/dist/commands/idea.js +2 -50
- package/dist/commands/project.js +2 -47
- package/dist/commands/roadmap.js +0 -107
- package/dist/commands/task.js +11 -17
- package/dist/commands/vm.js +0 -225
- package/dist/commands/workflow.js +4 -60
- package/dist/index.js +5 -1
- package/dist/lib/biz/billbee-types.d.ts +259 -0
- package/dist/lib/biz/billbee-types.js +41 -0
- package/dist/lib/biz/billbee.d.ts +37 -0
- package/dist/lib/biz/billbee.js +165 -0
- package/dist/lib/biz/embeddings.d.ts +45 -0
- package/dist/lib/biz/embeddings.js +115 -0
- package/dist/lib/biz/index.d.ts +13 -0
- package/dist/lib/biz/index.js +11 -0
- package/dist/lib/biz/qdrant.d.ts +52 -0
- package/dist/lib/biz/qdrant.js +158 -0
- package/dist/lib/biz/seatable-types.d.ts +115 -0
- package/dist/lib/biz/seatable-types.js +27 -0
- package/dist/lib/biz/seatable.d.ts +49 -0
- package/dist/lib/biz/seatable.js +210 -0
- package/dist/lib/biz/zendesk-types.d.ts +136 -0
- package/dist/lib/biz/zendesk-types.js +28 -0
- package/dist/lib/biz/zendesk.d.ts +45 -0
- package/dist/lib/biz/zendesk.js +206 -0
- package/package.json +2 -2
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Husky Biz Tickets Command
|
|
3
|
+
*
|
|
4
|
+
* Manages support tickets via Zendesk API
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { ZendeskClient } from "../../lib/biz/index.js";
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
export const ticketsCommand = new Command("tickets")
|
|
11
|
+
.description("Manage support tickets (Zendesk)");
|
|
12
|
+
// husky biz tickets list
|
|
13
|
+
ticketsCommand
|
|
14
|
+
.command("list")
|
|
15
|
+
.description("List tickets")
|
|
16
|
+
.option("-s, --status <status>", "Filter by status (new, open, pending, solved, closed)")
|
|
17
|
+
.option("-l, --limit <num>", "Number of tickets", "25")
|
|
18
|
+
.option("--json", "Output as JSON")
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
try {
|
|
21
|
+
const client = ZendeskClient.fromConfig();
|
|
22
|
+
const tickets = await client.listTickets({
|
|
23
|
+
per_page: parseInt(options.limit, 10),
|
|
24
|
+
status: options.status,
|
|
25
|
+
});
|
|
26
|
+
if (options.json) {
|
|
27
|
+
console.log(JSON.stringify(tickets, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(`\n 🎫 Tickets (${tickets.length})\n`);
|
|
31
|
+
if (tickets.length === 0) {
|
|
32
|
+
console.log(" No tickets found.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
for (const ticket of tickets) {
|
|
36
|
+
const statusIcon = getStatusIcon(ticket.status);
|
|
37
|
+
const date = new Date(ticket.updated_at).toLocaleDateString("de-DE");
|
|
38
|
+
console.log(` ${statusIcon} #${String(ticket.id).padEnd(8)} │ ${ticket.status.padEnd(8)} │ ${date} │ ${ticket.subject.slice(0, 45)}`);
|
|
39
|
+
}
|
|
40
|
+
console.log("");
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error("Error:", error.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
// husky biz tickets get <id>
|
|
48
|
+
ticketsCommand
|
|
49
|
+
.command("get <id>")
|
|
50
|
+
.description("Get ticket with conversation history")
|
|
51
|
+
.option("--json", "Output as JSON")
|
|
52
|
+
.action(async (id, options) => {
|
|
53
|
+
try {
|
|
54
|
+
const client = ZendeskClient.fromConfig();
|
|
55
|
+
const ticketId = parseInt(id, 10);
|
|
56
|
+
const [ticket, comments] = await Promise.all([
|
|
57
|
+
client.getTicket(ticketId),
|
|
58
|
+
client.getTicketComments(ticketId),
|
|
59
|
+
]);
|
|
60
|
+
if (options.json) {
|
|
61
|
+
console.log(JSON.stringify({ ticket, comments }, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log(`\n Ticket #${ticket.id}: ${ticket.subject}`);
|
|
65
|
+
console.log(" " + "─".repeat(60));
|
|
66
|
+
console.log(` Status: ${ticket.status}`);
|
|
67
|
+
console.log(` Priority: ${ticket.priority || "normal"}`);
|
|
68
|
+
console.log(` Created: ${new Date(ticket.created_at).toLocaleString("de-DE")}`);
|
|
69
|
+
console.log(` Updated: ${new Date(ticket.updated_at).toLocaleString("de-DE")}`);
|
|
70
|
+
if (ticket.tags && ticket.tags.length > 0) {
|
|
71
|
+
console.log(` Tags: ${ticket.tags.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
console.log(`\n Conversation (${comments.length} messages):`);
|
|
74
|
+
console.log(" " + "─".repeat(60));
|
|
75
|
+
for (const comment of comments) {
|
|
76
|
+
const date = new Date(comment.created_at).toLocaleString("de-DE");
|
|
77
|
+
const visibility = comment.public ? "Public" : "Internal";
|
|
78
|
+
console.log(`\n [${date}] (${visibility})`);
|
|
79
|
+
console.log(` ${comment.body.slice(0, 500)}${comment.body.length > 500 ? "..." : ""}`);
|
|
80
|
+
if (comment.attachments && comment.attachments.length > 0) {
|
|
81
|
+
console.log(` 📎 ${comment.attachments.length} attachment(s)`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
console.log("");
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error("Error:", error.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// husky biz tickets reply <id> -m <message>
|
|
92
|
+
ticketsCommand
|
|
93
|
+
.command("reply <id>")
|
|
94
|
+
.description("Reply to a ticket (public)")
|
|
95
|
+
.requiredOption("-m, --message <text>", "Reply message")
|
|
96
|
+
.action(async (id, options) => {
|
|
97
|
+
try {
|
|
98
|
+
const client = ZendeskClient.fromConfig();
|
|
99
|
+
const ticket = await client.addComment(parseInt(id, 10), options.message, true);
|
|
100
|
+
console.log(`✓ Public reply added to ticket #${ticket.id}`);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error("Error:", error.message);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// husky biz tickets note <id> -m <message>
|
|
108
|
+
ticketsCommand
|
|
109
|
+
.command("note <id>")
|
|
110
|
+
.description("Add internal note to ticket")
|
|
111
|
+
.requiredOption("-m, --message <text>", "Note content")
|
|
112
|
+
.action(async (id, options) => {
|
|
113
|
+
try {
|
|
114
|
+
const client = ZendeskClient.fromConfig();
|
|
115
|
+
const ticket = await client.addInternalNote(parseInt(id, 10), options.message);
|
|
116
|
+
console.log(`✓ Internal note added to ticket #${ticket.id}`);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error("Error:", error.message);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
// husky biz tickets update <id>
|
|
124
|
+
ticketsCommand
|
|
125
|
+
.command("update <id>")
|
|
126
|
+
.description("Update ticket properties")
|
|
127
|
+
.option("--status <status>", "New status (open, pending, solved)")
|
|
128
|
+
.option("--priority <priority>", "New priority (low, normal, high, urgent)")
|
|
129
|
+
.action(async (id, options) => {
|
|
130
|
+
try {
|
|
131
|
+
const client = ZendeskClient.fromConfig();
|
|
132
|
+
const updates = {};
|
|
133
|
+
if (options.status)
|
|
134
|
+
updates.status = options.status;
|
|
135
|
+
if (options.priority)
|
|
136
|
+
updates.priority = options.priority;
|
|
137
|
+
if (Object.keys(updates).length === 0) {
|
|
138
|
+
console.error("Error: Provide --status or --priority");
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
const ticket = await client.updateTicket(parseInt(id, 10), updates);
|
|
142
|
+
console.log(`✓ Updated ticket #${ticket.id}`);
|
|
143
|
+
console.log(` Status: ${ticket.status}, Priority: ${ticket.priority || "normal"}`);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error("Error:", error.message);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// husky biz tickets close <id>
|
|
151
|
+
ticketsCommand
|
|
152
|
+
.command("close <id>")
|
|
153
|
+
.description("Close/solve a ticket")
|
|
154
|
+
.action(async (id) => {
|
|
155
|
+
try {
|
|
156
|
+
const client = ZendeskClient.fromConfig();
|
|
157
|
+
const ticket = await client.closeTicket(parseInt(id, 10));
|
|
158
|
+
console.log(`✓ Ticket #${ticket.id} marked as solved`);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
console.error("Error:", error.message);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// husky biz tickets attachments <id>
|
|
166
|
+
ticketsCommand
|
|
167
|
+
.command("attachments <id>")
|
|
168
|
+
.description("List attachments for a ticket")
|
|
169
|
+
.option("--json", "Output as JSON")
|
|
170
|
+
.action(async (id, options) => {
|
|
171
|
+
try {
|
|
172
|
+
const client = ZendeskClient.fromConfig();
|
|
173
|
+
const attachments = await client.getTicketAttachments(parseInt(id, 10));
|
|
174
|
+
if (options.json) {
|
|
175
|
+
console.log(JSON.stringify(attachments, null, 2));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
console.log(`\n 📎 Attachments for ticket #${id} (${attachments.length})\n`);
|
|
179
|
+
if (attachments.length === 0) {
|
|
180
|
+
console.log(" No attachments found.");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
for (const att of attachments) {
|
|
184
|
+
const size = (att.size / 1024).toFixed(1);
|
|
185
|
+
console.log(` ${att.id} │ ${att.file_name} │ ${size} KB │ ${att.content_type}`);
|
|
186
|
+
}
|
|
187
|
+
console.log("");
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.error("Error:", error.message);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
// husky biz tickets download <id> <filename>
|
|
195
|
+
ticketsCommand
|
|
196
|
+
.command("download <ticketId> <filename>")
|
|
197
|
+
.description("Download an attachment")
|
|
198
|
+
.option("-o, --output <path>", "Output path")
|
|
199
|
+
.action(async (ticketId, filename, options) => {
|
|
200
|
+
try {
|
|
201
|
+
const client = ZendeskClient.fromConfig();
|
|
202
|
+
const attachments = await client.getTicketAttachments(parseInt(ticketId, 10));
|
|
203
|
+
const attachment = attachments.find(a => a.file_name === filename || a.id === parseInt(filename, 10));
|
|
204
|
+
if (!attachment) {
|
|
205
|
+
console.error(`Attachment "${filename}" not found in ticket #${ticketId}`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
const data = await client.downloadAttachment(attachment.content_url);
|
|
209
|
+
const outputPath = options.output || path.join(process.cwd(), attachment.file_name);
|
|
210
|
+
fs.writeFileSync(outputPath, Buffer.from(data));
|
|
211
|
+
console.log(`✓ Downloaded: ${outputPath}`);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.error("Error:", error.message);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
// Helper
|
|
219
|
+
function getStatusIcon(status) {
|
|
220
|
+
switch (status) {
|
|
221
|
+
case "new": return "🆕";
|
|
222
|
+
case "open": return "📬";
|
|
223
|
+
case "pending": return "⏳";
|
|
224
|
+
case "hold": return "⏸️";
|
|
225
|
+
case "solved": return "✅";
|
|
226
|
+
case "closed": return "🔒";
|
|
227
|
+
default: return "○";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// husky biz tickets create
|
|
231
|
+
ticketsCommand
|
|
232
|
+
.command("create")
|
|
233
|
+
.description("Create a new ticket")
|
|
234
|
+
.requiredOption("-s, --subject <text>", "Ticket subject")
|
|
235
|
+
.requiredOption("-m, --message <text>", "Initial message/description")
|
|
236
|
+
.option("-e, --email <email>", "Requester email")
|
|
237
|
+
.option("-p, --priority <priority>", "Priority (low, normal, high, urgent)")
|
|
238
|
+
.option("--json", "Output as JSON")
|
|
239
|
+
.action(async (options) => {
|
|
240
|
+
try {
|
|
241
|
+
const client = ZendeskClient.fromConfig();
|
|
242
|
+
const ticketData = {
|
|
243
|
+
subject: options.subject,
|
|
244
|
+
comment: { body: options.message },
|
|
245
|
+
};
|
|
246
|
+
if (options.email) {
|
|
247
|
+
ticketData.requester = { email: options.email };
|
|
248
|
+
}
|
|
249
|
+
if (options.priority) {
|
|
250
|
+
ticketData.priority = options.priority;
|
|
251
|
+
}
|
|
252
|
+
const ticket = await client.createTicket(ticketData);
|
|
253
|
+
if (options.json) {
|
|
254
|
+
console.log(JSON.stringify(ticket, null, 2));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
console.log(`✓ Ticket created: #${ticket.id}`);
|
|
258
|
+
console.log(` Subject: ${ticket.subject}`);
|
|
259
|
+
console.log(` Status: ${ticket.status}`);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.error("Error:", error.message);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
// husky biz tickets search <query>
|
|
267
|
+
ticketsCommand
|
|
268
|
+
.command("search <query>")
|
|
269
|
+
.description("Search tickets (Zendesk search syntax)")
|
|
270
|
+
.option("--json", "Output as JSON")
|
|
271
|
+
.action(async (query, options) => {
|
|
272
|
+
try {
|
|
273
|
+
const client = ZendeskClient.fromConfig();
|
|
274
|
+
const tickets = await client.searchTickets(query);
|
|
275
|
+
if (options.json) {
|
|
276
|
+
console.log(JSON.stringify(tickets, null, 2));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
console.log(`\n 🔍 Search: "${query}" (${tickets.length} results)\n`);
|
|
280
|
+
if (tickets.length === 0) {
|
|
281
|
+
console.log(" No tickets found.");
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
for (const ticket of tickets) {
|
|
285
|
+
const statusIcon = getStatusIcon(String(ticket.status));
|
|
286
|
+
console.log(` ${statusIcon} #${String(ticket.id).padEnd(8)} │ ${String(ticket.status).padEnd(8)} │ ${ticket.subject.slice(0, 45)}`);
|
|
287
|
+
}
|
|
288
|
+
console.log("");
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
console.error("Error:", error.message);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
// husky biz tickets delete <id>
|
|
296
|
+
ticketsCommand
|
|
297
|
+
.command("delete <id>")
|
|
298
|
+
.description("Delete a ticket (soft delete)")
|
|
299
|
+
.action(async (id) => {
|
|
300
|
+
try {
|
|
301
|
+
const client = ZendeskClient.fromConfig();
|
|
302
|
+
await client.deleteTicket(parseInt(id, 10));
|
|
303
|
+
console.log(`✓ Ticket #${id} deleted`);
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
console.error("Error:", error.message);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
// husky biz tickets assign <id> <userId>
|
|
311
|
+
ticketsCommand
|
|
312
|
+
.command("assign <id> <userId>")
|
|
313
|
+
.description("Assign ticket to a user")
|
|
314
|
+
.action(async (id, userId) => {
|
|
315
|
+
try {
|
|
316
|
+
const client = ZendeskClient.fromConfig();
|
|
317
|
+
const ticket = await client.assignTicket(parseInt(id, 10), parseInt(userId, 10));
|
|
318
|
+
console.log(`✓ Ticket #${ticket.id} assigned to user #${userId}`);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
console.error("Error:", error.message);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
// husky biz tickets tags <id> add/remove <tag>
|
|
326
|
+
const tagsCommand = ticketsCommand
|
|
327
|
+
.command("tags <id>")
|
|
328
|
+
.description("Manage ticket tags");
|
|
329
|
+
tagsCommand
|
|
330
|
+
.command("add <tag>")
|
|
331
|
+
.description("Add a tag to ticket")
|
|
332
|
+
.action(async (tag, options, command) => {
|
|
333
|
+
try {
|
|
334
|
+
const client = ZendeskClient.fromConfig();
|
|
335
|
+
const ticketId = parseInt(command.parent.args[0], 10);
|
|
336
|
+
const ticket = await client.addTags(ticketId, [tag]);
|
|
337
|
+
console.log(`✓ Tag "${tag}" added to ticket #${ticket.id}`);
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
console.error("Error:", error.message);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
tagsCommand
|
|
345
|
+
.command("remove <tag>")
|
|
346
|
+
.description("Remove a tag from ticket")
|
|
347
|
+
.action(async (tag, options, command) => {
|
|
348
|
+
try {
|
|
349
|
+
const client = ZendeskClient.fromConfig();
|
|
350
|
+
const ticketId = parseInt(command.parent.args[0], 10);
|
|
351
|
+
const ticket = await client.removeTags(ticketId, [tag]);
|
|
352
|
+
console.log(`✓ Tag "${tag}" removed from ticket #${ticket.id}`);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
console.error("Error:", error.message);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
// husky biz tickets macros
|
|
360
|
+
const macrosCommand = ticketsCommand
|
|
361
|
+
.command("macros")
|
|
362
|
+
.description("Manage Zendesk macros (templates)");
|
|
363
|
+
macrosCommand
|
|
364
|
+
.command("list")
|
|
365
|
+
.description("List available macros")
|
|
366
|
+
.option("--json", "Output as JSON")
|
|
367
|
+
.action(async (options) => {
|
|
368
|
+
try {
|
|
369
|
+
const client = ZendeskClient.fromConfig();
|
|
370
|
+
const macros = await client.listMacros({ active: true });
|
|
371
|
+
if (options.json) {
|
|
372
|
+
console.log(JSON.stringify(macros, null, 2));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
console.log(`\n 📋 Macros (${macros.length})\n`);
|
|
376
|
+
if (macros.length === 0) {
|
|
377
|
+
console.log(" No macros found.");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
for (const macro of macros) {
|
|
381
|
+
console.log(` #${String(macro.id).padEnd(8)} │ ${macro.title.slice(0, 50)}`);
|
|
382
|
+
}
|
|
383
|
+
console.log("");
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
console.error("Error:", error.message);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
macrosCommand
|
|
391
|
+
.command("get <id>")
|
|
392
|
+
.description("Get macro details")
|
|
393
|
+
.option("--json", "Output as JSON")
|
|
394
|
+
.action(async (id, options) => {
|
|
395
|
+
try {
|
|
396
|
+
const client = ZendeskClient.fromConfig();
|
|
397
|
+
const macro = await client.getMacro(parseInt(id, 10));
|
|
398
|
+
if (options.json) {
|
|
399
|
+
console.log(JSON.stringify(macro, null, 2));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
console.log(`\n Macro #${macro.id}: ${macro.title}`);
|
|
403
|
+
console.log(" " + "─".repeat(50));
|
|
404
|
+
console.log(` Active: ${macro.active ? "Yes" : "No"}`);
|
|
405
|
+
if (macro.description) {
|
|
406
|
+
console.log(` Description: ${macro.description}`);
|
|
407
|
+
}
|
|
408
|
+
console.log(`\n Actions:`);
|
|
409
|
+
for (const action of macro.actions) {
|
|
410
|
+
console.log(` ${action.field}: ${typeof action.value === 'string' ? action.value.slice(0, 60) : JSON.stringify(action.value)}`);
|
|
411
|
+
}
|
|
412
|
+
console.log("");
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
console.error("Error:", error.message);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
macrosCommand
|
|
420
|
+
.command("apply <ticketId> <macroId>")
|
|
421
|
+
.description("Apply a macro to a ticket")
|
|
422
|
+
.action(async (ticketId, macroId) => {
|
|
423
|
+
try {
|
|
424
|
+
const client = ZendeskClient.fromConfig();
|
|
425
|
+
const ticket = await client.applyMacro(parseInt(ticketId, 10), parseInt(macroId, 10));
|
|
426
|
+
console.log(`✓ Macro #${macroId} applied to ticket #${ticket.id}`);
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
console.error("Error:", error.message);
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
// ============================================================================
|
|
434
|
+
// PREBUILD SEMANTIC SEARCH COMMANDS
|
|
435
|
+
// ============================================================================
|
|
436
|
+
import { EmbeddingService, QdrantClient } from "../../lib/biz/index.js";
|
|
437
|
+
const TICKET_COLLECTION = "zendesk_tickets_01";
|
|
438
|
+
const LEARNED_COLLECTION = "tickets_learned";
|
|
439
|
+
// husky biz tickets similar <id>
|
|
440
|
+
ticketsCommand
|
|
441
|
+
.command("similar <id>")
|
|
442
|
+
.description("[PREBUILD] Find tickets similar to given ticket")
|
|
443
|
+
.option("-l, --limit <num>", "Number of results", "5")
|
|
444
|
+
.option("-c, --collection <name>", "Qdrant collection", TICKET_COLLECTION)
|
|
445
|
+
.option("--json", "Output as JSON")
|
|
446
|
+
.action(async (id, options) => {
|
|
447
|
+
try {
|
|
448
|
+
const zendesk = ZendeskClient.fromConfig();
|
|
449
|
+
const embeddings = EmbeddingService.fromConfig();
|
|
450
|
+
const qdrant = QdrantClient.fromConfig();
|
|
451
|
+
// 1. Fetch ticket from Zendesk
|
|
452
|
+
console.log(` Fetching ticket #${id}...`);
|
|
453
|
+
const ticket = await zendesk.getTicket(parseInt(id, 10));
|
|
454
|
+
// 2. Create embedding from subject + description
|
|
455
|
+
const text = `${ticket.subject}\n${ticket.description || ''}`;
|
|
456
|
+
console.log(` Generating embedding...`);
|
|
457
|
+
const vector = await embeddings.embed(text);
|
|
458
|
+
// 3. Search Qdrant
|
|
459
|
+
console.log(` Searching ${options.collection}...`);
|
|
460
|
+
const results = await qdrant.search(options.collection, vector, parseInt(options.limit, 10) + 1 // +1 to exclude self
|
|
461
|
+
);
|
|
462
|
+
// Filter out the same ticket
|
|
463
|
+
const filteredResults = results.filter(r => {
|
|
464
|
+
const payload = r.payload;
|
|
465
|
+
return payload?.ticket_id !== ticket.id && payload?.id !== ticket.id;
|
|
466
|
+
}).slice(0, parseInt(options.limit, 10));
|
|
467
|
+
if (options.json) {
|
|
468
|
+
console.log(JSON.stringify({
|
|
469
|
+
success: true,
|
|
470
|
+
source_ticket: { id: ticket.id, subject: ticket.subject },
|
|
471
|
+
similar: filteredResults.map(r => ({
|
|
472
|
+
id: r.payload?.ticket_id || r.id,
|
|
473
|
+
score: r.score,
|
|
474
|
+
subject: r.payload?.subject,
|
|
475
|
+
status: r.payload?.status,
|
|
476
|
+
})),
|
|
477
|
+
}, null, 2));
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
console.log(`\n 🔍 Tickets similar to #${ticket.id}: "${ticket.subject}"\n`);
|
|
481
|
+
if (filteredResults.length === 0) {
|
|
482
|
+
console.log(" No similar tickets found.");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
for (const result of filteredResults) {
|
|
486
|
+
const payload = result.payload;
|
|
487
|
+
const ticketId = payload?.ticket_id || payload?.id || result.id;
|
|
488
|
+
const subject = payload?.subject || '(no subject)';
|
|
489
|
+
const status = payload?.status || '';
|
|
490
|
+
console.log(` [${(result.score * 100).toFixed(1)}%] #${ticketId} │ ${status} │ ${String(subject).slice(0, 45)}`);
|
|
491
|
+
}
|
|
492
|
+
console.log("");
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
console.error("Error:", error.message);
|
|
496
|
+
process.exit(1);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
// husky biz tickets find-similar "<query>"
|
|
500
|
+
ticketsCommand
|
|
501
|
+
.command("find-similar <query>")
|
|
502
|
+
.description("[PREBUILD] Semantic search tickets by text query")
|
|
503
|
+
.option("-l, --limit <num>", "Number of results", "5")
|
|
504
|
+
.option("-c, --collection <name>", "Qdrant collection", TICKET_COLLECTION)
|
|
505
|
+
.option("--json", "Output as JSON")
|
|
506
|
+
.action(async (query, options) => {
|
|
507
|
+
try {
|
|
508
|
+
const embeddings = EmbeddingService.fromConfig();
|
|
509
|
+
const qdrant = QdrantClient.fromConfig();
|
|
510
|
+
// 1. Create embedding from query
|
|
511
|
+
console.log(` Generating embedding for query...`);
|
|
512
|
+
const vector = await embeddings.embed(query);
|
|
513
|
+
// 2. Search Qdrant
|
|
514
|
+
console.log(` Searching ${options.collection}...`);
|
|
515
|
+
const results = await qdrant.search(options.collection, vector, parseInt(options.limit, 10));
|
|
516
|
+
if (options.json) {
|
|
517
|
+
console.log(JSON.stringify({
|
|
518
|
+
success: true,
|
|
519
|
+
query,
|
|
520
|
+
results: results.map(r => ({
|
|
521
|
+
id: r.payload?.ticket_id || r.id,
|
|
522
|
+
score: r.score,
|
|
523
|
+
subject: r.payload?.subject,
|
|
524
|
+
status: r.payload?.status,
|
|
525
|
+
})),
|
|
526
|
+
}, null, 2));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
console.log(`\n 🔍 Semantic search: "${query}"\n`);
|
|
530
|
+
if (results.length === 0) {
|
|
531
|
+
console.log(" No matching tickets found.");
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
for (const result of results) {
|
|
535
|
+
const payload = result.payload;
|
|
536
|
+
const ticketId = payload?.ticket_id || payload?.id || result.id;
|
|
537
|
+
const subject = payload?.subject || '(no subject)';
|
|
538
|
+
const status = payload?.status || '';
|
|
539
|
+
console.log(` [${(result.score * 100).toFixed(1)}%] #${ticketId} │ ${status} │ ${String(subject).slice(0, 45)}`);
|
|
540
|
+
}
|
|
541
|
+
console.log("");
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
console.error("Error:", error.message);
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
// husky biz tickets knowledge "<query>"
|
|
549
|
+
ticketsCommand
|
|
550
|
+
.command("knowledge <query>")
|
|
551
|
+
.description("[PREBUILD] Search resolved tickets for solutions")
|
|
552
|
+
.option("-l, --limit <num>", "Number of results", "3")
|
|
553
|
+
.option("--json", "Output as JSON")
|
|
554
|
+
.action(async (query, options) => {
|
|
555
|
+
try {
|
|
556
|
+
const embeddings = EmbeddingService.fromConfig();
|
|
557
|
+
const qdrant = QdrantClient.fromConfig();
|
|
558
|
+
// 1. Create embedding from query
|
|
559
|
+
console.log(` Generating embedding...`);
|
|
560
|
+
const vector = await embeddings.embed(query);
|
|
561
|
+
// 2. Search learned tickets collection
|
|
562
|
+
console.log(` Searching ${LEARNED_COLLECTION}...`);
|
|
563
|
+
const results = await qdrant.search(LEARNED_COLLECTION, vector, parseInt(options.limit, 10));
|
|
564
|
+
if (options.json) {
|
|
565
|
+
console.log(JSON.stringify({
|
|
566
|
+
success: true,
|
|
567
|
+
query,
|
|
568
|
+
knowledge: results.map(r => ({
|
|
569
|
+
id: r.payload?.ticket_id || r.id,
|
|
570
|
+
score: r.score,
|
|
571
|
+
subject: r.payload?.subject,
|
|
572
|
+
resolution: r.payload?.resolution || r.payload?.solution,
|
|
573
|
+
})),
|
|
574
|
+
}, null, 2));
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
console.log(`\n 📚 Knowledge search: "${query}"\n`);
|
|
578
|
+
if (results.length === 0) {
|
|
579
|
+
console.log(" No relevant knowledge found.");
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
for (const result of results) {
|
|
583
|
+
const payload = result.payload;
|
|
584
|
+
const ticketId = payload?.ticket_id || payload?.id || result.id;
|
|
585
|
+
const subject = payload?.subject || '(no subject)';
|
|
586
|
+
const resolution = payload?.resolution || payload?.solution || '';
|
|
587
|
+
console.log(` [${(result.score * 100).toFixed(1)}%] #${ticketId}`);
|
|
588
|
+
console.log(` Subject: ${String(subject).slice(0, 60)}`);
|
|
589
|
+
if (resolution) {
|
|
590
|
+
console.log(` Resolution: ${String(resolution).slice(0, 100)}...`);
|
|
591
|
+
}
|
|
592
|
+
console.log("");
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
console.error("Error:", error.message);
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
export default ticketsCommand;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Husky Biz Command
|
|
3
|
+
*
|
|
4
|
+
* Business operations for autonomous agents
|
|
5
|
+
* Integrates with Billbee, Zendesk, SeaTable, and Qdrant
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { ordersCommand } from "./biz/orders.js";
|
|
9
|
+
import { productsCommand } from "./biz/products.js";
|
|
10
|
+
import { ticketsCommand } from "./biz/tickets.js";
|
|
11
|
+
import { customersCommand } from "./biz/customers.js";
|
|
12
|
+
import { seatableCommand } from "./biz/seatable.js";
|
|
13
|
+
import { qdrantCommand } from "./biz/qdrant.js";
|
|
14
|
+
export const bizCommand = new Command("biz")
|
|
15
|
+
.description("Business operations for autonomous agents")
|
|
16
|
+
.addCommand(ordersCommand)
|
|
17
|
+
.addCommand(productsCommand)
|
|
18
|
+
.addCommand(ticketsCommand)
|
|
19
|
+
.addCommand(customersCommand)
|
|
20
|
+
.addCommand(seatableCommand)
|
|
21
|
+
.addCommand(qdrantCommand);
|
|
22
|
+
export default bizCommand;
|
|
@@ -4,6 +4,19 @@ interface Config {
|
|
|
4
4
|
apiKey?: string;
|
|
5
5
|
workerId?: string;
|
|
6
6
|
workerName?: string;
|
|
7
|
+
billbeeApiKey?: string;
|
|
8
|
+
billbeeUsername?: string;
|
|
9
|
+
billbeePassword?: string;
|
|
10
|
+
billbeeBaseUrl?: string;
|
|
11
|
+
zendeskSubdomain?: string;
|
|
12
|
+
zendeskEmail?: string;
|
|
13
|
+
zendeskApiToken?: string;
|
|
14
|
+
seatableApiToken?: string;
|
|
15
|
+
seatableServerUrl?: string;
|
|
16
|
+
qdrantUrl?: string;
|
|
17
|
+
qdrantApiKey?: string;
|
|
18
|
+
gcpProjectId?: string;
|
|
19
|
+
gcpLocation?: string;
|
|
7
20
|
}
|
|
8
21
|
export declare function getConfig(): Config;
|
|
9
22
|
export declare function setConfig(key: "apiUrl" | "apiKey" | "workerId" | "workerName", value: string): void;
|