@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.
Files changed (44) hide show
  1. package/README.md +3 -116
  2. package/dist/commands/biz/customers.d.ts +8 -0
  3. package/dist/commands/biz/customers.js +181 -0
  4. package/dist/commands/biz/orders.d.ts +8 -0
  5. package/dist/commands/biz/orders.js +226 -0
  6. package/dist/commands/biz/products.d.ts +8 -0
  7. package/dist/commands/biz/products.js +255 -0
  8. package/dist/commands/biz/qdrant.d.ts +8 -0
  9. package/dist/commands/biz/qdrant.js +170 -0
  10. package/dist/commands/biz/seatable.d.ts +8 -0
  11. package/dist/commands/biz/seatable.js +449 -0
  12. package/dist/commands/biz/tickets.d.ts +8 -0
  13. package/dist/commands/biz/tickets.js +600 -0
  14. package/dist/commands/biz.d.ts +9 -0
  15. package/dist/commands/biz.js +22 -0
  16. package/dist/commands/config.d.ts +13 -0
  17. package/dist/commands/config.js +43 -16
  18. package/dist/commands/explain.js +12 -595
  19. package/dist/commands/idea.js +2 -50
  20. package/dist/commands/project.js +2 -47
  21. package/dist/commands/roadmap.js +0 -107
  22. package/dist/commands/task.js +11 -17
  23. package/dist/commands/vm.js +0 -225
  24. package/dist/commands/workflow.js +4 -60
  25. package/dist/index.js +5 -1
  26. package/dist/lib/biz/billbee-types.d.ts +259 -0
  27. package/dist/lib/biz/billbee-types.js +41 -0
  28. package/dist/lib/biz/billbee.d.ts +37 -0
  29. package/dist/lib/biz/billbee.js +165 -0
  30. package/dist/lib/biz/embeddings.d.ts +45 -0
  31. package/dist/lib/biz/embeddings.js +115 -0
  32. package/dist/lib/biz/index.d.ts +13 -0
  33. package/dist/lib/biz/index.js +11 -0
  34. package/dist/lib/biz/qdrant.d.ts +52 -0
  35. package/dist/lib/biz/qdrant.js +158 -0
  36. package/dist/lib/biz/seatable-types.d.ts +115 -0
  37. package/dist/lib/biz/seatable-types.js +27 -0
  38. package/dist/lib/biz/seatable.d.ts +49 -0
  39. package/dist/lib/biz/seatable.js +210 -0
  40. package/dist/lib/biz/zendesk-types.d.ts +136 -0
  41. package/dist/lib/biz/zendesk-types.js +28 -0
  42. package/dist/lib/biz/zendesk.d.ts +45 -0
  43. package/dist/lib/biz/zendesk.js +206 -0
  44. package/package.json +2 -2
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Zendesk API Types
3
+ * Extended for v0.9 with macros support
4
+ */
5
+ export interface ZendeskConfig {
6
+ SUBDOMAIN: string;
7
+ EMAIL: string;
8
+ API_TOKEN: string;
9
+ BASE_URL?: string;
10
+ }
11
+ export declare enum TicketPriority {
12
+ Low = "low",
13
+ Normal = "normal",
14
+ High = "high",
15
+ Urgent = "urgent"
16
+ }
17
+ export declare enum TicketStatus {
18
+ New = "new",
19
+ Open = "open",
20
+ Pending = "pending",
21
+ Hold = "hold",
22
+ Solved = "solved",
23
+ Closed = "closed"
24
+ }
25
+ export declare enum TicketType {
26
+ Problem = "problem",
27
+ Incident = "incident",
28
+ Question = "question",
29
+ Task = "task"
30
+ }
31
+ export interface PaginationParams {
32
+ page?: number;
33
+ per_page?: number;
34
+ sort_by?: string;
35
+ sort_order?: 'asc' | 'desc';
36
+ }
37
+ export interface ZendeskUser {
38
+ id: number;
39
+ name: string;
40
+ email: string;
41
+ phone?: string;
42
+ role: string;
43
+ created_at: string;
44
+ updated_at: string;
45
+ organization_id?: number;
46
+ tags?: string[];
47
+ }
48
+ export interface ZendeskTicket {
49
+ id: number;
50
+ subject: string;
51
+ description?: string;
52
+ status: TicketStatus | string;
53
+ priority?: TicketPriority | string;
54
+ type?: TicketType | string;
55
+ requester_id?: number;
56
+ assignee_id?: number;
57
+ group_id?: number;
58
+ tags?: string[];
59
+ custom_fields?: Array<{
60
+ id: number;
61
+ value: unknown;
62
+ }>;
63
+ created_at: string;
64
+ updated_at: string;
65
+ url?: string;
66
+ }
67
+ export interface TicketComment {
68
+ id: number;
69
+ type: string;
70
+ author_id: number;
71
+ body: string;
72
+ html_body?: string;
73
+ public: boolean;
74
+ created_at: string;
75
+ attachments?: Attachment[];
76
+ }
77
+ export interface Attachment {
78
+ id: number;
79
+ file_name: string;
80
+ content_url: string;
81
+ content_type: string;
82
+ size: number;
83
+ }
84
+ export interface CreateTicketRequest {
85
+ subject: string;
86
+ comment: {
87
+ body: string;
88
+ public?: boolean;
89
+ };
90
+ requester?: {
91
+ name?: string;
92
+ email?: string;
93
+ };
94
+ requester_id?: number;
95
+ priority?: TicketPriority | string;
96
+ status?: TicketStatus | string;
97
+ type?: TicketType | string;
98
+ tags?: string[];
99
+ assignee_id?: number;
100
+ }
101
+ export interface UpdateTicketRequest {
102
+ subject?: string;
103
+ comment?: {
104
+ body: string;
105
+ public?: boolean;
106
+ };
107
+ status?: TicketStatus | string;
108
+ priority?: TicketPriority | string;
109
+ assignee_id?: number;
110
+ tags?: string[];
111
+ }
112
+ export interface SearchResult<T> {
113
+ results: T[];
114
+ count: number;
115
+ next_page: string | null;
116
+ previous_page: string | null;
117
+ }
118
+ export interface MacroAction {
119
+ field: string;
120
+ value: string | string[];
121
+ }
122
+ export interface ZendeskMacro {
123
+ id: number;
124
+ title: string;
125
+ description?: string;
126
+ active: boolean;
127
+ actions: MacroAction[];
128
+ restriction?: {
129
+ type: string;
130
+ id?: number;
131
+ ids?: number[];
132
+ };
133
+ created_at: string;
134
+ updated_at: string;
135
+ url?: string;
136
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Zendesk API Types
3
+ * Extended for v0.9 with macros support
4
+ */
5
+ // Enums
6
+ export var TicketPriority;
7
+ (function (TicketPriority) {
8
+ TicketPriority["Low"] = "low";
9
+ TicketPriority["Normal"] = "normal";
10
+ TicketPriority["High"] = "high";
11
+ TicketPriority["Urgent"] = "urgent";
12
+ })(TicketPriority || (TicketPriority = {}));
13
+ export var TicketStatus;
14
+ (function (TicketStatus) {
15
+ TicketStatus["New"] = "new";
16
+ TicketStatus["Open"] = "open";
17
+ TicketStatus["Pending"] = "pending";
18
+ TicketStatus["Hold"] = "hold";
19
+ TicketStatus["Solved"] = "solved";
20
+ TicketStatus["Closed"] = "closed";
21
+ })(TicketStatus || (TicketStatus = {}));
22
+ export var TicketType;
23
+ (function (TicketType) {
24
+ TicketType["Problem"] = "problem";
25
+ TicketType["Incident"] = "incident";
26
+ TicketType["Question"] = "question";
27
+ TicketType["Task"] = "task";
28
+ })(TicketType || (TicketType = {}));
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Zendesk API Client
3
+ * Extended for v0.9 with macros and enhanced ticket operations
4
+ */
5
+ import { ZendeskConfig, ZendeskTicket, ZendeskUser, TicketComment, CreateTicketRequest, UpdateTicketRequest, PaginationParams, Attachment, ZendeskMacro } from './zendesk-types.js';
6
+ export declare class ZendeskClient {
7
+ private config;
8
+ private baseUrl;
9
+ constructor(config: ZendeskConfig);
10
+ /**
11
+ * Create client from Husky config
12
+ */
13
+ static fromConfig(): ZendeskClient;
14
+ /**
15
+ * Make authenticated request
16
+ */
17
+ private request;
18
+ listTickets(params?: PaginationParams & {
19
+ status?: string;
20
+ }): Promise<ZendeskTicket[]>;
21
+ getTicket(ticketId: number): Promise<ZendeskTicket>;
22
+ createTicket(data: CreateTicketRequest): Promise<ZendeskTicket>;
23
+ updateTicket(ticketId: number, data: UpdateTicketRequest): Promise<ZendeskTicket>;
24
+ deleteTicket(ticketId: number): Promise<void>;
25
+ addComment(ticketId: number, body: string, isPublic?: boolean): Promise<ZendeskTicket>;
26
+ addInternalNote(ticketId: number, body: string): Promise<ZendeskTicket>;
27
+ closeTicket(ticketId: number): Promise<ZendeskTicket>;
28
+ assignTicket(ticketId: number, assigneeId: number): Promise<ZendeskTicket>;
29
+ addTags(ticketId: number, tags: string[]): Promise<ZendeskTicket>;
30
+ removeTags(ticketId: number, tags: string[]): Promise<ZendeskTicket>;
31
+ getTicketComments(ticketId: number): Promise<TicketComment[]>;
32
+ searchTickets(query: string): Promise<ZendeskTicket[]>;
33
+ getTicketAttachments(ticketId: number): Promise<Attachment[]>;
34
+ downloadAttachment(contentUrl: string): Promise<ArrayBuffer>;
35
+ getUser(userId: number): Promise<ZendeskUser>;
36
+ searchUsers(query: string): Promise<ZendeskUser[]>;
37
+ getUserTickets(userId: number): Promise<ZendeskTicket[]>;
38
+ listMacros(params?: {
39
+ active?: boolean;
40
+ category?: number;
41
+ }): Promise<ZendeskMacro[]>;
42
+ getMacro(macroId: number): Promise<ZendeskMacro>;
43
+ applyMacro(ticketId: number, macroId: number): Promise<ZendeskTicket>;
44
+ }
45
+ export default ZendeskClient;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Zendesk API Client
3
+ * Extended for v0.9 with macros and enhanced ticket operations
4
+ */
5
+ import { getConfig } from '../../commands/config.js';
6
+ export class ZendeskClient {
7
+ config;
8
+ baseUrl;
9
+ constructor(config) {
10
+ this.config = config;
11
+ this.baseUrl = config.BASE_URL || `https://${config.SUBDOMAIN}.zendesk.com/api/v2`;
12
+ }
13
+ /**
14
+ * Create client from Husky config
15
+ */
16
+ static fromConfig() {
17
+ const config = getConfig();
18
+ const zendeskConfig = {
19
+ SUBDOMAIN: config.zendeskSubdomain || process.env.ZENDESK_SUBDOMAIN || '',
20
+ EMAIL: config.zendeskEmail || process.env.ZENDESK_EMAIL || '',
21
+ API_TOKEN: config.zendeskApiToken || process.env.ZENDESK_API_TOKEN || '',
22
+ };
23
+ if (!zendeskConfig.SUBDOMAIN || !zendeskConfig.EMAIL || !zendeskConfig.API_TOKEN) {
24
+ throw new Error('Missing Zendesk credentials. Configure with:\n' +
25
+ ' husky config set zendesk-subdomain <subdomain>\n' +
26
+ ' husky config set zendesk-email <email>\n' +
27
+ ' husky config set zendesk-api-token <token>');
28
+ }
29
+ return new ZendeskClient(zendeskConfig);
30
+ }
31
+ /**
32
+ * Make authenticated request
33
+ */
34
+ async request(endpoint, options = {}) {
35
+ const url = `${this.baseUrl}${endpoint}`;
36
+ const auth = Buffer.from(`${this.config.EMAIL}/token:${this.config.API_TOKEN}`).toString('base64');
37
+ const response = await fetch(url, {
38
+ ...options,
39
+ headers: {
40
+ 'Authorization': `Basic ${auth}`,
41
+ 'Content-Type': 'application/json',
42
+ ...options.headers,
43
+ },
44
+ });
45
+ if (!response.ok) {
46
+ const error = await response.text();
47
+ throw new Error(`Zendesk API Error ${response.status}: ${error}`);
48
+ }
49
+ if (response.status === 204) {
50
+ return {};
51
+ }
52
+ return response.json();
53
+ }
54
+ // =========================================================================
55
+ // TICKETS
56
+ // =========================================================================
57
+ async listTickets(params) {
58
+ const query = new URLSearchParams();
59
+ if (params?.page)
60
+ query.set('page', String(params.page));
61
+ if (params?.per_page)
62
+ query.set('per_page', String(params.per_page));
63
+ if (params?.sort_by)
64
+ query.set('sort_by', params.sort_by);
65
+ if (params?.sort_order)
66
+ query.set('sort_order', params.sort_order);
67
+ // Filter by status using search
68
+ if (params?.status) {
69
+ return this.searchTickets(`status:${params.status}`);
70
+ }
71
+ const endpoint = `/tickets.json${query.toString() ? `?${query}` : ''}`;
72
+ const response = await this.request(endpoint);
73
+ return response.tickets;
74
+ }
75
+ async getTicket(ticketId) {
76
+ const response = await this.request(`/tickets/${ticketId}.json`);
77
+ return response.ticket;
78
+ }
79
+ async createTicket(data) {
80
+ const response = await this.request('/tickets.json', {
81
+ method: 'POST',
82
+ body: JSON.stringify({ ticket: data }),
83
+ });
84
+ return response.ticket;
85
+ }
86
+ async updateTicket(ticketId, data) {
87
+ const response = await this.request(`/tickets/${ticketId}.json`, {
88
+ method: 'PUT',
89
+ body: JSON.stringify({ ticket: data }),
90
+ });
91
+ return response.ticket;
92
+ }
93
+ async deleteTicket(ticketId) {
94
+ await this.request(`/tickets/${ticketId}.json`, { method: 'DELETE' });
95
+ }
96
+ async addComment(ticketId, body, isPublic = true) {
97
+ return this.updateTicket(ticketId, {
98
+ comment: { body, public: isPublic },
99
+ });
100
+ }
101
+ async addInternalNote(ticketId, body) {
102
+ return this.addComment(ticketId, body, false);
103
+ }
104
+ async closeTicket(ticketId) {
105
+ return this.updateTicket(ticketId, { status: 'solved' });
106
+ }
107
+ async assignTicket(ticketId, assigneeId) {
108
+ return this.updateTicket(ticketId, { assignee_id: assigneeId });
109
+ }
110
+ async addTags(ticketId, tags) {
111
+ const ticket = await this.getTicket(ticketId);
112
+ const existingTags = ticket.tags || [];
113
+ const newTags = [...new Set([...existingTags, ...tags])];
114
+ return this.updateTicket(ticketId, { tags: newTags });
115
+ }
116
+ async removeTags(ticketId, tags) {
117
+ const ticket = await this.getTicket(ticketId);
118
+ const existingTags = ticket.tags || [];
119
+ const newTags = existingTags.filter(t => !tags.includes(t));
120
+ return this.updateTicket(ticketId, { tags: newTags });
121
+ }
122
+ async getTicketComments(ticketId) {
123
+ const response = await this.request(`/tickets/${ticketId}/comments.json`);
124
+ return response.comments;
125
+ }
126
+ async searchTickets(query) {
127
+ const searchParams = new URLSearchParams({ query: `type:ticket ${query}` });
128
+ const response = await this.request(`/search.json?${searchParams}`);
129
+ return response.results;
130
+ }
131
+ // =========================================================================
132
+ // ATTACHMENTS
133
+ // =========================================================================
134
+ async getTicketAttachments(ticketId) {
135
+ const comments = await this.getTicketComments(ticketId);
136
+ const attachments = [];
137
+ for (const comment of comments) {
138
+ if (comment.attachments) {
139
+ attachments.push(...comment.attachments);
140
+ }
141
+ }
142
+ return attachments;
143
+ }
144
+ async downloadAttachment(contentUrl) {
145
+ const auth = Buffer.from(`${this.config.EMAIL}/token:${this.config.API_TOKEN}`).toString('base64');
146
+ const response = await fetch(contentUrl, {
147
+ headers: { 'Authorization': `Basic ${auth}` },
148
+ });
149
+ if (!response.ok) {
150
+ throw new Error(`Failed to download attachment: ${response.status}`);
151
+ }
152
+ return response.arrayBuffer();
153
+ }
154
+ // =========================================================================
155
+ // USERS
156
+ // =========================================================================
157
+ async getUser(userId) {
158
+ const response = await this.request(`/users/${userId}.json`);
159
+ return response.user;
160
+ }
161
+ async searchUsers(query) {
162
+ const searchParams = new URLSearchParams({ query });
163
+ const response = await this.request(`/users/search.json?${searchParams}`);
164
+ return response.users;
165
+ }
166
+ async getUserTickets(userId) {
167
+ const response = await this.request(`/users/${userId}/tickets/requested.json`);
168
+ return response.tickets;
169
+ }
170
+ // =========================================================================
171
+ // MACROS (Templates)
172
+ // =========================================================================
173
+ async listMacros(params) {
174
+ const query = new URLSearchParams();
175
+ if (params?.active !== undefined)
176
+ query.set('active', String(params.active));
177
+ if (params?.category)
178
+ query.set('category', String(params.category));
179
+ const endpoint = `/macros.json${query.toString() ? `?${query}` : ''}`;
180
+ const response = await this.request(endpoint);
181
+ return response.macros;
182
+ }
183
+ async getMacro(macroId) {
184
+ const response = await this.request(`/macros/${macroId}.json`);
185
+ return response.macro;
186
+ }
187
+ async applyMacro(ticketId, macroId) {
188
+ // Get macro to extract its actions
189
+ const macro = await this.getMacro(macroId);
190
+ // Build update from macro actions
191
+ const update = {};
192
+ for (const action of macro.actions) {
193
+ if (action.field === 'comment_value' || action.field === 'comment_value_html') {
194
+ update.comment = { body: String(action.value), public: true };
195
+ }
196
+ else if (action.field === 'status') {
197
+ update.status = String(action.value);
198
+ }
199
+ else if (action.field === 'priority') {
200
+ update.priority = String(action.value);
201
+ }
202
+ }
203
+ return this.updateTicket(ticketId, update);
204
+ }
205
+ }
206
+ export default ZendeskClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonfestl/husky-cli",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "CLI for Huskyv0 Task Orchestration with Claude Agent SDK",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,4 +42,4 @@
42
42
  "bugs": {
43
43
  "url": "https://github.com/simon-sfxecom/huskyv0/issues"
44
44
  }
45
- }
45
+ }