@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,449 @@
1
+ /**
2
+ * Husky Biz SeaTable Command
3
+ *
4
+ * Manages SeaTable Base data for Supply Chain workflows
5
+ */
6
+ import { Command } from "commander";
7
+ import { SeaTableClient } from "../../lib/biz/index.js";
8
+ export const seatableCommand = new Command("seatable")
9
+ .description("Manage SeaTable data (Supply Chain)");
10
+ // husky biz seatable tables
11
+ seatableCommand
12
+ .command("tables")
13
+ .description("List all tables in the Base")
14
+ .option("--json", "Output as JSON")
15
+ .action(async (options) => {
16
+ try {
17
+ const client = SeaTableClient.fromConfig();
18
+ const metadata = await client.getMetadata();
19
+ if (options.json) {
20
+ console.log(JSON.stringify(metadata.tables, null, 2));
21
+ return;
22
+ }
23
+ console.log(`\n 📊 SeaTable Base Tables (${metadata.tables.length})\n`);
24
+ for (const table of metadata.tables) {
25
+ console.log(` 📋 ${table.name}`);
26
+ console.log(` ID: ${table._id} | Columns: ${table.columns.length}`);
27
+ }
28
+ console.log("");
29
+ }
30
+ catch (error) {
31
+ console.error("Error:", error.message);
32
+ process.exit(1);
33
+ }
34
+ });
35
+ // husky biz seatable columns <table>
36
+ seatableCommand
37
+ .command("columns <table>")
38
+ .description("Show columns for a table")
39
+ .option("--json", "Output as JSON")
40
+ .action(async (tableName, options) => {
41
+ try {
42
+ const client = SeaTableClient.fromConfig();
43
+ const metadata = await client.getMetadata();
44
+ const table = metadata.tables.find(t => t.name === tableName);
45
+ if (!table) {
46
+ console.error(`Table "${tableName}" not found.`);
47
+ console.log("Available tables:", metadata.tables.map(t => t.name).join(", "));
48
+ process.exit(1);
49
+ }
50
+ if (options.json) {
51
+ console.log(JSON.stringify(table.columns, null, 2));
52
+ return;
53
+ }
54
+ console.log(`\n 📋 Columns in "${tableName}"\n`);
55
+ for (const col of table.columns) {
56
+ const typeStr = String(col.type).padEnd(15);
57
+ console.log(` ${col.name.padEnd(25)} │ ${typeStr} │ ${col.key}`);
58
+ }
59
+ console.log("");
60
+ }
61
+ catch (error) {
62
+ console.error("Error:", error.message);
63
+ process.exit(1);
64
+ }
65
+ });
66
+ // husky biz seatable list <table>
67
+ seatableCommand
68
+ .command("list <table>")
69
+ .description("List rows from a table")
70
+ .option("-l, --limit <num>", "Number of rows", "20")
71
+ .option("--start <num>", "Start offset", "0")
72
+ .option("--order-by <col>", "Order by column")
73
+ .option("--desc", "Descending order")
74
+ .option("-v, --view <name>", "View name")
75
+ .option("--json", "Output as JSON")
76
+ .action(async (tableName, options) => {
77
+ try {
78
+ const client = SeaTableClient.fromConfig();
79
+ const rows = await client.listRows({
80
+ table_name: tableName,
81
+ limit: parseInt(options.limit, 10),
82
+ start: parseInt(options.start, 10),
83
+ order_by: options.orderBy,
84
+ direction: options.desc ? 'desc' : undefined,
85
+ view_name: options.view,
86
+ });
87
+ if (options.json) {
88
+ console.log(JSON.stringify(rows, null, 2));
89
+ return;
90
+ }
91
+ console.log(`\n 📋 ${tableName} (${rows.length} rows)\n`);
92
+ if (rows.length === 0) {
93
+ console.log(" No rows found.");
94
+ return;
95
+ }
96
+ // Display first few columns of each row
97
+ for (const row of rows) {
98
+ const displayCols = Object.entries(row)
99
+ .filter(([k]) => !k.startsWith('_'))
100
+ .slice(0, 4)
101
+ .map(([k, v]) => `${k}: ${formatValue(v)}`)
102
+ .join(' │ ');
103
+ console.log(` [${row._id.slice(0, 8)}] ${displayCols}`);
104
+ }
105
+ console.log("");
106
+ }
107
+ catch (error) {
108
+ console.error("Error:", error.message);
109
+ process.exit(1);
110
+ }
111
+ });
112
+ // husky biz seatable get <table> <rowId>
113
+ seatableCommand
114
+ .command("get <table> <rowId>")
115
+ .description("Get a single row by ID")
116
+ .option("--json", "Output as JSON")
117
+ .action(async (tableName, rowId, options) => {
118
+ try {
119
+ const client = SeaTableClient.fromConfig();
120
+ const row = await client.getRow(tableName, rowId);
121
+ if (!row) {
122
+ console.error(`Row "${rowId}" not found in table "${tableName}".`);
123
+ process.exit(1);
124
+ }
125
+ if (options.json) {
126
+ console.log(JSON.stringify(row, null, 2));
127
+ return;
128
+ }
129
+ console.log(`\n 📋 Row ${rowId}\n`);
130
+ for (const [key, value] of Object.entries(row)) {
131
+ if (!key.startsWith('_')) {
132
+ console.log(` ${key.padEnd(20)}: ${formatValue(value)}`);
133
+ }
134
+ }
135
+ console.log(`\n Created: ${row._ctime || 'N/A'}`);
136
+ console.log(` Modified: ${row._mtime || 'N/A'}\n`);
137
+ }
138
+ catch (error) {
139
+ console.error("Error:", error.message);
140
+ process.exit(1);
141
+ }
142
+ });
143
+ // husky biz seatable search <table> <query>
144
+ seatableCommand
145
+ .command("search <table> <query>")
146
+ .description("Full-text search in a table")
147
+ .option("--json", "Output as JSON")
148
+ .action(async (tableName, query, options) => {
149
+ try {
150
+ const client = SeaTableClient.fromConfig();
151
+ const rows = await client.searchRows(tableName, query);
152
+ if (options.json) {
153
+ console.log(JSON.stringify(rows, null, 2));
154
+ return;
155
+ }
156
+ console.log(`\n 🔍 Search "${query}" in ${tableName} (${rows.length} results)\n`);
157
+ if (rows.length === 0) {
158
+ console.log(" No matching rows found.");
159
+ return;
160
+ }
161
+ for (const row of rows) {
162
+ const displayCols = Object.entries(row)
163
+ .filter(([k]) => !k.startsWith('_'))
164
+ .slice(0, 4)
165
+ .map(([k, v]) => `${k}: ${formatValue(v)}`)
166
+ .join(' │ ');
167
+ console.log(` [${row._id.slice(0, 8)}] ${displayCols}`);
168
+ }
169
+ console.log("");
170
+ }
171
+ catch (error) {
172
+ console.error("Error:", error.message);
173
+ process.exit(1);
174
+ }
175
+ });
176
+ // husky biz seatable create <table>
177
+ seatableCommand
178
+ .command("create <table>")
179
+ .description("Create a new row")
180
+ .requiredOption("-d, --data <json>", "Row data as JSON")
181
+ .option("--json", "Output created row as JSON")
182
+ .action(async (tableName, options) => {
183
+ try {
184
+ const client = SeaTableClient.fromConfig();
185
+ let data;
186
+ try {
187
+ data = JSON.parse(options.data);
188
+ }
189
+ catch {
190
+ console.error("Error: Invalid JSON data");
191
+ console.log('Example: --data \'{"Name":"Test","Status":"Active"}\'');
192
+ process.exit(1);
193
+ }
194
+ const row = await client.appendRow(tableName, data);
195
+ if (options.json) {
196
+ console.log(JSON.stringify(row, null, 2));
197
+ return;
198
+ }
199
+ console.log(`✓ Created row in ${tableName}`);
200
+ console.log(` ID: ${row._id}`);
201
+ }
202
+ catch (error) {
203
+ console.error("Error:", error.message);
204
+ process.exit(1);
205
+ }
206
+ });
207
+ // husky biz seatable update <table> <rowId>
208
+ seatableCommand
209
+ .command("update <table> <rowId>")
210
+ .description("Update a row")
211
+ .requiredOption("-d, --data <json>", "Update data as JSON")
212
+ .action(async (tableName, rowId, options) => {
213
+ try {
214
+ const client = SeaTableClient.fromConfig();
215
+ let data;
216
+ try {
217
+ data = JSON.parse(options.data);
218
+ }
219
+ catch {
220
+ console.error("Error: Invalid JSON data");
221
+ console.log('Example: --data \'{"Status":"Completed"}\'');
222
+ process.exit(1);
223
+ }
224
+ await client.updateRow(tableName, rowId, data);
225
+ console.log(`✓ Updated row ${rowId} in ${tableName}`);
226
+ }
227
+ catch (error) {
228
+ console.error("Error:", error.message);
229
+ process.exit(1);
230
+ }
231
+ });
232
+ // husky biz seatable delete <table> <rowId>
233
+ seatableCommand
234
+ .command("delete <table> <rowId>")
235
+ .description("Delete a row")
236
+ .option("-f, --force", "Skip confirmation")
237
+ .action(async (tableName, rowId, options) => {
238
+ try {
239
+ const client = SeaTableClient.fromConfig();
240
+ if (!options.force) {
241
+ console.log(`⚠️ About to delete row ${rowId} from ${tableName}`);
242
+ console.log(" Use --force to skip this confirmation");
243
+ process.exit(0);
244
+ }
245
+ await client.deleteRow(tableName, rowId);
246
+ console.log(`✓ Deleted row ${rowId} from ${tableName}`);
247
+ }
248
+ catch (error) {
249
+ console.error("Error:", error.message);
250
+ process.exit(1);
251
+ }
252
+ });
253
+ // Helper: format value for display
254
+ function formatValue(value) {
255
+ if (value === null || value === undefined)
256
+ return '-';
257
+ if (typeof value === 'string')
258
+ return value.slice(0, 40) + (value.length > 40 ? '...' : '');
259
+ if (typeof value === 'number')
260
+ return String(value);
261
+ if (typeof value === 'boolean')
262
+ return value ? '✓' : '✗';
263
+ if (Array.isArray(value))
264
+ return `[${value.length} items]`;
265
+ if (typeof value === 'object')
266
+ return '[object]';
267
+ return String(value);
268
+ }
269
+ // ============================================================================
270
+ // PREBUILD COMMANDS (Agent-Focused)
271
+ // ============================================================================
272
+ import { EmbeddingService, QdrantClient } from "../../lib/biz/index.js";
273
+ const PRODUCTS_COLLECTION = "supplychain_nocodb_products";
274
+ // husky biz seatable find-product "<query>"
275
+ seatableCommand
276
+ .command("find-product <query>")
277
+ .description("[PREBUILD] Semantic search for products")
278
+ .option("-l, --limit <num>", "Number of results", "5")
279
+ .option("--json", "Output as JSON")
280
+ .action(async (query, options) => {
281
+ try {
282
+ const embeddings = EmbeddingService.fromConfig();
283
+ const qdrant = QdrantClient.fromConfig();
284
+ // 1. Create embedding from query
285
+ console.log(` Generating embedding for "${query}"...`);
286
+ const vector = await embeddings.embed(query);
287
+ // 2. Search Qdrant products collection (uses named vector 'product_name')
288
+ console.log(` Searching ${PRODUCTS_COLLECTION}...`);
289
+ const results = await qdrant.search(PRODUCTS_COLLECTION, vector, parseInt(options.limit, 10), { vectorName: 'product_name' });
290
+ if (options.json) {
291
+ console.log(JSON.stringify({
292
+ success: true,
293
+ query,
294
+ products: results.map(r => ({
295
+ id: r.id,
296
+ score: r.score,
297
+ ...r.payload,
298
+ })),
299
+ }, null, 2));
300
+ return;
301
+ }
302
+ console.log(`\n 🔍 Product search: "${query}"\n`);
303
+ if (results.length === 0) {
304
+ console.log(" No products found.");
305
+ return;
306
+ }
307
+ for (const result of results) {
308
+ const payload = result.payload;
309
+ const sku = payload?.sku || payload?.SKU || '';
310
+ const name = payload?.name || payload?.Name || payload?.title || '';
311
+ console.log(` [${(result.score * 100).toFixed(1)}%] ${sku} │ ${String(name).slice(0, 50)}`);
312
+ }
313
+ console.log("");
314
+ }
315
+ catch (error) {
316
+ console.error("Error:", error.message);
317
+ process.exit(1);
318
+ }
319
+ });
320
+ // husky biz seatable find-supplier "<query>"
321
+ seatableCommand
322
+ .command("find-supplier <query>")
323
+ .description("[PREBUILD] Text search for suppliers")
324
+ .option("-l, --limit <num>", "Number of results", "10")
325
+ .option("--json", "Output as JSON")
326
+ .action(async (query, options) => {
327
+ try {
328
+ const client = SeaTableClient.fromConfig();
329
+ // List all suppliers and filter client-side
330
+ console.log(` Searching Suppliers for "${query}"...`);
331
+ const allRows = await client.listRows({ table_name: "Suppliers", limit: 100 });
332
+ const lowerQuery = query.toLowerCase();
333
+ const filtered = allRows.filter(row => {
334
+ const name = String(row.Name || row['0000'] || '').toLowerCase();
335
+ const tag = String(row.TAG || '').toLowerCase();
336
+ return name.includes(lowerQuery) || tag.includes(lowerQuery);
337
+ });
338
+ const limitedRows = filtered.slice(0, parseInt(options.limit, 10));
339
+ if (options.json) {
340
+ console.log(JSON.stringify({ success: true, query, suppliers: limitedRows }, null, 2));
341
+ return;
342
+ }
343
+ console.log(`\n 🔍 Supplier search: "${query}" (${limitedRows.length} results)\n`);
344
+ if (limitedRows.length === 0) {
345
+ console.log(" No suppliers found.");
346
+ return;
347
+ }
348
+ for (const row of limitedRows) {
349
+ const name = row.Name || row['0000'] || '';
350
+ const tag = row.TAG || '';
351
+ const country = row.Country || '';
352
+ console.log(` [${row._id.slice(0, 8)}] ${name} │ ${tag} │ ${country}`);
353
+ }
354
+ console.log("");
355
+ }
356
+ catch (error) {
357
+ console.error("Error:", error.message);
358
+ process.exit(1);
359
+ }
360
+ });
361
+ // husky biz seatable order-status <orderNo>
362
+ seatableCommand
363
+ .command("order-status <orderNo>")
364
+ .description("[PREBUILD] Get order with all positions")
365
+ .option("--json", "Output as JSON")
366
+ .action(async (orderNo, options) => {
367
+ try {
368
+ const client = SeaTableClient.fromConfig();
369
+ // 1. Find order in Orders table
370
+ console.log(` Looking up order ${orderNo}...`);
371
+ const orders = await client.searchRows("Orders", orderNo);
372
+ if (orders.length === 0) {
373
+ console.error(`Order "${orderNo}" not found.`);
374
+ process.exit(1);
375
+ }
376
+ const order = orders[0];
377
+ // 2. Get order positions
378
+ console.log(` Fetching order positions...`);
379
+ let positions = [];
380
+ try {
381
+ positions = await client.searchRows("Order Positions", orderNo);
382
+ }
383
+ catch {
384
+ // Table might not exist or be empty
385
+ }
386
+ if (options.json) {
387
+ console.log(JSON.stringify({ success: true, order, positions }, null, 2));
388
+ return;
389
+ }
390
+ console.log(`\n 📦 Order: ${orderNo}`);
391
+ console.log(" " + "─".repeat(50));
392
+ // Display order fields
393
+ for (const [key, value] of Object.entries(order)) {
394
+ if (!key.startsWith('_')) {
395
+ console.log(` ${key}: ${formatValue(value)}`);
396
+ }
397
+ }
398
+ if (positions.length > 0) {
399
+ console.log(`\n 📋 Positions (${positions.length}):`);
400
+ console.log(" " + "─".repeat(50));
401
+ for (const pos of positions) {
402
+ const sku = pos.SKU || pos.sku || '';
403
+ const name = pos.ProductName || pos.name || '';
404
+ const qty = pos.Qty || pos.qty || '';
405
+ console.log(` ${sku} │ ${qty}x │ ${String(name).slice(0, 35)}`);
406
+ }
407
+ }
408
+ console.log("");
409
+ }
410
+ catch (error) {
411
+ console.error("Error:", error.message);
412
+ process.exit(1);
413
+ }
414
+ });
415
+ // husky biz seatable stock-check <sku>
416
+ seatableCommand
417
+ .command("stock-check <sku>")
418
+ .description("[PREBUILD] Check stock across suppliers")
419
+ .option("--json", "Output as JSON")
420
+ .action(async (sku, options) => {
421
+ try {
422
+ const client = SeaTableClient.fromConfig();
423
+ // Search Supplier_Sources for this SKU
424
+ console.log(` Checking suppliers for SKU "${sku}"...`);
425
+ const sources = await client.searchRows("Supplier_Sources", sku);
426
+ if (options.json) {
427
+ console.log(JSON.stringify({ success: true, sku, sources }, null, 2));
428
+ return;
429
+ }
430
+ console.log(`\n 📦 Stock check: ${sku}\n`);
431
+ if (sources.length === 0) {
432
+ console.log(" No supplier sources found for this SKU.");
433
+ return;
434
+ }
435
+ for (const source of sources) {
436
+ const supplier = source.Supplier || source.supplier_name || '';
437
+ const price = source.Price || source.price || '-';
438
+ const stock = source.Stock || source.stock || source.available || '-';
439
+ const leadTime = source.LeadTime || source['Delivery Time'] || '-';
440
+ console.log(` ${String(supplier).padEnd(20)} │ ${String(price).padEnd(8)} │ Stock: ${stock} │ Lead: ${leadTime}d`);
441
+ }
442
+ console.log("");
443
+ }
444
+ catch (error) {
445
+ console.error("Error:", error.message);
446
+ process.exit(1);
447
+ }
448
+ });
449
+ export default seatableCommand;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Husky Biz Tickets Command
3
+ *
4
+ * Manages support tickets via Zendesk API
5
+ */
6
+ import { Command } from "commander";
7
+ export declare const ticketsCommand: Command;
8
+ export default ticketsCommand;