@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,255 @@
1
+ /**
2
+ * Husky Biz Products Command
3
+ *
4
+ * Manages products via Billbee API
5
+ */
6
+ import { Command } from "commander";
7
+ import { BillbeeClient } from "../../lib/biz/index.js";
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import { homedir } from "os";
11
+ export const productsCommand = new Command("products")
12
+ .description("Manage products (Billbee)");
13
+ // husky biz products list
14
+ productsCommand
15
+ .command("list")
16
+ .description("List products")
17
+ .option("-l, --limit <num>", "Number of products", "20")
18
+ .option("-p, --page <num>", "Page number", "1")
19
+ .option("--low-stock", "Show only low stock products")
20
+ .option("--json", "Output as JSON")
21
+ .action(async (options) => {
22
+ try {
23
+ const client = BillbeeClient.fromConfig();
24
+ const response = await client.listProducts({
25
+ page: parseInt(options.page, 10),
26
+ pageSize: parseInt(options.limit, 10),
27
+ });
28
+ let products = response.Data;
29
+ // Filter low stock if requested
30
+ if (options.lowStock) {
31
+ products = products.filter(p => p.LowStock === true);
32
+ }
33
+ if (options.json) {
34
+ console.log(JSON.stringify(products, null, 2));
35
+ return;
36
+ }
37
+ console.log(`\n 📦 Products (${response.Paging.TotalRows} total)\n`);
38
+ if (products.length === 0) {
39
+ console.log(" No products found.");
40
+ return;
41
+ }
42
+ for (const product of products) {
43
+ const sku = (product.SKU || "-").padEnd(15);
44
+ const title = (product.Title || "Unknown").slice(0, 35).padEnd(35);
45
+ const stock = String(product.StockCurrent ?? 0).padStart(5);
46
+ const price = `€${(product.Price ?? 0).toFixed(2)}`;
47
+ const lowStock = product.LowStock ? "⚠️" : " ";
48
+ console.log(` ${lowStock} ${sku} │ ${title} │ Stock: ${stock} │ ${price}`);
49
+ }
50
+ console.log(`\n Page ${response.Paging.Page} of ${response.Paging.TotalPages}\n`);
51
+ }
52
+ catch (error) {
53
+ console.error("Error:", error.message);
54
+ process.exit(1);
55
+ }
56
+ });
57
+ // husky biz products get <id>
58
+ productsCommand
59
+ .command("get <id>")
60
+ .description("Get product details by ID")
61
+ .option("--json", "Output as JSON")
62
+ .action(async (id, options) => {
63
+ try {
64
+ const client = BillbeeClient.fromConfig();
65
+ const response = await client.getProduct(id);
66
+ const product = response.Data;
67
+ if (options.json) {
68
+ console.log(JSON.stringify(product, null, 2));
69
+ return;
70
+ }
71
+ printProductDetails(product);
72
+ }
73
+ catch (error) {
74
+ console.error("Error:", error.message);
75
+ process.exit(1);
76
+ }
77
+ });
78
+ // husky biz products search <sku>
79
+ productsCommand
80
+ .command("search <sku>")
81
+ .description("Find product by SKU")
82
+ .option("--json", "Output as JSON")
83
+ .action(async (sku, options) => {
84
+ try {
85
+ const client = BillbeeClient.fromConfig();
86
+ const product = await client.getProductBySku(sku);
87
+ if (!product) {
88
+ console.log(`\n Product with SKU "${sku}" not found.\n`);
89
+ process.exit(1);
90
+ }
91
+ if (options.json) {
92
+ console.log(JSON.stringify(product, null, 2));
93
+ return;
94
+ }
95
+ printProductDetails(product);
96
+ }
97
+ catch (error) {
98
+ console.error("Error:", error.message);
99
+ process.exit(1);
100
+ }
101
+ });
102
+ // husky biz products stock <id>
103
+ productsCommand
104
+ .command("stock <id>")
105
+ .description("Show stock info for product")
106
+ .option("--json", "Output as JSON")
107
+ .action(async (id, options) => {
108
+ try {
109
+ const client = BillbeeClient.fromConfig();
110
+ const response = await client.getProduct(id);
111
+ const product = response.Data;
112
+ if (options.json) {
113
+ console.log(JSON.stringify({
114
+ id: product.Id,
115
+ sku: product.SKU,
116
+ stockCurrent: product.StockCurrent,
117
+ stockDesired: product.StockDesired,
118
+ stockWarning: product.StockWarning,
119
+ lowStock: product.LowStock,
120
+ stocks: product.Stocks,
121
+ }, null, 2));
122
+ return;
123
+ }
124
+ console.log(`\n 📊 Stock for ${product.SKU || `#${product.Id}`}`);
125
+ console.log(" " + "─".repeat(40));
126
+ console.log(` Current: ${product.StockCurrent ?? 0}`);
127
+ console.log(` Desired: ${product.StockDesired ?? 0}`);
128
+ console.log(` Warning: ${product.StockWarning ?? 0}`);
129
+ console.log(` Low Stock: ${product.LowStock ? "⚠️ YES" : "✅ No"}`);
130
+ if (product.Stocks && product.Stocks.length > 0) {
131
+ console.log(`\n Warehouses:`);
132
+ for (const stock of product.Stocks) {
133
+ console.log(` ${stock.Name}: ${stock.StockCurrent}`);
134
+ }
135
+ }
136
+ console.log("");
137
+ }
138
+ catch (error) {
139
+ console.error("Error:", error.message);
140
+ process.exit(1);
141
+ }
142
+ });
143
+ // husky biz products set-stock <id> <qty> --reason
144
+ productsCommand
145
+ .command("set-stock <id> <quantity>")
146
+ .description("Update stock quantity (with audit reason)")
147
+ .requiredOption("-r, --reason <text>", "Reason for stock change (required)")
148
+ .action(async (id, quantity, options) => {
149
+ try {
150
+ const client = BillbeeClient.fromConfig();
151
+ // Get current stock for logging
152
+ const before = await client.getProduct(id);
153
+ const oldStock = before.Data.StockCurrent ?? 0;
154
+ // Update stock
155
+ const response = await client.updateProduct(id, {
156
+ StockDesired: parseInt(quantity, 10),
157
+ });
158
+ const product = response.Data;
159
+ const newStock = parseInt(quantity, 10);
160
+ // Log the change
161
+ logStockChange({
162
+ productId: String(id),
163
+ sku: product.SKU || "",
164
+ before: oldStock,
165
+ after: newStock,
166
+ reason: options.reason,
167
+ timestamp: new Date().toISOString(),
168
+ });
169
+ console.log(`✓ Stock updated for ${product.SKU || id}: ${oldStock} → ${newStock}`);
170
+ console.log(` Reason: ${options.reason}`);
171
+ }
172
+ catch (error) {
173
+ console.error("Error:", error.message);
174
+ process.exit(1);
175
+ }
176
+ });
177
+ // husky biz products set-price <id> <price>
178
+ productsCommand
179
+ .command("set-price <id> <price>")
180
+ .description("Update product price")
181
+ .action(async (id, price, options) => {
182
+ try {
183
+ const client = BillbeeClient.fromConfig();
184
+ const response = await client.updateProduct(id, {
185
+ Price: parseFloat(price),
186
+ });
187
+ const product = response.Data;
188
+ console.log(`✓ Price updated for ${product.SKU || id}: €${parseFloat(price).toFixed(2)}`);
189
+ }
190
+ catch (error) {
191
+ console.error("Error:", error.message);
192
+ process.exit(1);
193
+ }
194
+ });
195
+ // husky biz products update <id>
196
+ productsCommand
197
+ .command("update <id>")
198
+ .description("Update product fields")
199
+ .option("--title <text>", "Update title")
200
+ .option("--price <num>", "Update price")
201
+ .option("--ean <code>", "Update EAN")
202
+ .option("--description <text>", "Update description")
203
+ .action(async (id, options) => {
204
+ try {
205
+ const client = BillbeeClient.fromConfig();
206
+ const updates = {};
207
+ if (options.title)
208
+ updates.Title = options.title;
209
+ if (options.price)
210
+ updates.Price = parseFloat(options.price);
211
+ if (options.ean)
212
+ updates.EAN = options.ean;
213
+ if (options.description)
214
+ updates.Description = options.description;
215
+ if (Object.keys(updates).length === 0) {
216
+ console.error("Error: Provide at least one field to update");
217
+ console.log(" --title, --price, --ean, --description");
218
+ process.exit(1);
219
+ }
220
+ const response = await client.updateProduct(id, updates);
221
+ console.log(`✓ Product ${response.Data.SKU || id} updated`);
222
+ }
223
+ catch (error) {
224
+ console.error("Error:", error.message);
225
+ process.exit(1);
226
+ }
227
+ });
228
+ // Helper: Print product details
229
+ function printProductDetails(product) {
230
+ console.log(`\n Product #${product.Id}`);
231
+ console.log(" " + "─".repeat(50));
232
+ console.log(` SKU: ${product.SKU || "-"}`);
233
+ console.log(` EAN: ${product.EAN || "-"}`);
234
+ console.log(` Title: ${product.Title || "-"}`);
235
+ console.log(` Price: €${(product.Price ?? 0).toFixed(2)}`);
236
+ console.log(` Cost: €${(product.CostPrice ?? 0).toFixed(2)}`);
237
+ console.log(` Stock: ${product.StockCurrent ?? 0}`);
238
+ console.log(` Low Stock: ${product.LowStock ? "⚠️ YES" : "No"}`);
239
+ if (product.Category1) {
240
+ console.log(` Category: ${[product.Category1, product.Category2, product.Category3].filter(Boolean).join(" > ")}`);
241
+ }
242
+ if (product.Weight) {
243
+ console.log(` Weight: ${product.Weight}g`);
244
+ }
245
+ console.log("");
246
+ }
247
+ function logStockChange(change) {
248
+ const logDir = path.join(homedir(), ".husky");
249
+ const logFile = path.join(logDir, "stock-changes.log");
250
+ if (!fs.existsSync(logDir)) {
251
+ fs.mkdirSync(logDir, { recursive: true });
252
+ }
253
+ fs.appendFileSync(logFile, JSON.stringify(change) + "\n");
254
+ }
255
+ export default productsCommand;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Husky Biz Qdrant Command
3
+ *
4
+ * Vector database operations for semantic search
5
+ */
6
+ import { Command } from "commander";
7
+ export declare const qdrantCommand: Command;
8
+ export default qdrantCommand;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Husky Biz Qdrant Command
3
+ *
4
+ * Vector database operations for semantic search
5
+ */
6
+ import { Command } from "commander";
7
+ import { QdrantClient } from "../../lib/biz/index.js";
8
+ export const qdrantCommand = new Command("qdrant")
9
+ .description("Vector database operations (Qdrant)");
10
+ // husky biz qdrant collections
11
+ qdrantCommand
12
+ .command("collections")
13
+ .description("List all collections")
14
+ .option("--json", "Output as JSON")
15
+ .action(async (options) => {
16
+ try {
17
+ const client = QdrantClient.fromConfig();
18
+ const collections = await client.listCollections();
19
+ if (options.json) {
20
+ console.log(JSON.stringify(collections, null, 2));
21
+ return;
22
+ }
23
+ console.log(`\n 📊 Qdrant Collections (${collections.length})\n`);
24
+ for (const name of collections) {
25
+ try {
26
+ const info = await client.getCollection(name);
27
+ console.log(` 📁 ${name.padEnd(35)} │ ${info.pointsCount} points`);
28
+ }
29
+ catch {
30
+ console.log(` 📁 ${name}`);
31
+ }
32
+ }
33
+ console.log("");
34
+ }
35
+ catch (error) {
36
+ console.error("Error:", error.message);
37
+ process.exit(1);
38
+ }
39
+ });
40
+ // husky biz qdrant info <collection>
41
+ qdrantCommand
42
+ .command("info <collection>")
43
+ .description("Get collection info")
44
+ .option("--json", "Output as JSON")
45
+ .action(async (collection, options) => {
46
+ try {
47
+ const client = QdrantClient.fromConfig();
48
+ const info = await client.getCollection(collection);
49
+ if (options.json) {
50
+ console.log(JSON.stringify(info, null, 2));
51
+ return;
52
+ }
53
+ console.log(`\n 📁 Collection: ${info.name}`);
54
+ console.log(` Points: ${info.pointsCount}`);
55
+ console.log(` Vectors: ${info.vectorsCount}\n`);
56
+ }
57
+ catch (error) {
58
+ console.error("Error:", error.message);
59
+ process.exit(1);
60
+ }
61
+ });
62
+ // husky biz qdrant count <collection>
63
+ qdrantCommand
64
+ .command("count <collection>")
65
+ .description("Count points in collection")
66
+ .action(async (collection) => {
67
+ try {
68
+ const client = QdrantClient.fromConfig();
69
+ const count = await client.count(collection);
70
+ console.log(count);
71
+ }
72
+ catch (error) {
73
+ console.error("Error:", error.message);
74
+ process.exit(1);
75
+ }
76
+ });
77
+ // husky biz qdrant search <collection>
78
+ qdrantCommand
79
+ .command("search <collection>")
80
+ .description("Search with vector (input from stdin or --vector)")
81
+ .option("--vector <json>", "Vector as JSON array")
82
+ .option("-l, --limit <num>", "Number of results", "5")
83
+ .option("--json", "Output as JSON")
84
+ .action(async (collection, options) => {
85
+ try {
86
+ const client = QdrantClient.fromConfig();
87
+ let vector;
88
+ if (options.vector) {
89
+ vector = JSON.parse(options.vector);
90
+ }
91
+ else {
92
+ console.error("Error: --vector required (as JSON array)");
93
+ console.log("Example: --vector '[0.1, 0.2, ...]'");
94
+ process.exit(1);
95
+ }
96
+ const results = await client.search(collection, vector, parseInt(options.limit, 10));
97
+ if (options.json) {
98
+ console.log(JSON.stringify(results, null, 2));
99
+ return;
100
+ }
101
+ console.log(`\n 🔍 Search Results in ${collection} (${results.length})\n`);
102
+ for (const result of results) {
103
+ const score = result.score.toFixed(4);
104
+ const id = String(result.id).slice(0, 12);
105
+ const payloadPreview = result.payload
106
+ ? Object.entries(result.payload).slice(0, 2).map(([k, v]) => `${k}: ${String(v).slice(0, 30)}`).join(' │ ')
107
+ : '';
108
+ console.log(` [${id}] score: ${score} │ ${payloadPreview}`);
109
+ }
110
+ console.log("");
111
+ }
112
+ catch (error) {
113
+ console.error("Error:", error.message);
114
+ process.exit(1);
115
+ }
116
+ });
117
+ // husky biz qdrant get <collection> <id>
118
+ qdrantCommand
119
+ .command("get <collection> <id>")
120
+ .description("Get a point by ID")
121
+ .option("--json", "Output as JSON")
122
+ .action(async (collection, id, options) => {
123
+ try {
124
+ const client = QdrantClient.fromConfig();
125
+ const point = await client.getPoint(collection, id);
126
+ if (!point) {
127
+ console.error(`Point "${id}" not found in collection "${collection}".`);
128
+ process.exit(1);
129
+ }
130
+ if (options.json) {
131
+ console.log(JSON.stringify(point, null, 2));
132
+ return;
133
+ }
134
+ console.log(`\n 📊 Point ${id}`);
135
+ console.log(` Vector dimensions: ${point.vector.length}`);
136
+ if (point.payload) {
137
+ console.log(` Payload:`);
138
+ for (const [key, value] of Object.entries(point.payload)) {
139
+ console.log(` ${key}: ${String(value).slice(0, 60)}`);
140
+ }
141
+ }
142
+ console.log("");
143
+ }
144
+ catch (error) {
145
+ console.error("Error:", error.message);
146
+ process.exit(1);
147
+ }
148
+ });
149
+ // husky biz qdrant delete <collection> <id>
150
+ qdrantCommand
151
+ .command("delete <collection> <id>")
152
+ .description("Delete a point")
153
+ .option("-f, --force", "Skip confirmation")
154
+ .action(async (collection, id, options) => {
155
+ try {
156
+ const client = QdrantClient.fromConfig();
157
+ if (!options.force) {
158
+ console.log(`⚠️ About to delete point ${id} from ${collection}`);
159
+ console.log(" Use --force to skip this confirmation");
160
+ process.exit(0);
161
+ }
162
+ await client.deletePoints(collection, [id]);
163
+ console.log(`✓ Deleted point ${id} from ${collection}`);
164
+ }
165
+ catch (error) {
166
+ console.error("Error:", error.message);
167
+ process.exit(1);
168
+ }
169
+ });
170
+ export default qdrantCommand;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Husky Biz SeaTable Command
3
+ *
4
+ * Manages SeaTable Base data for Supply Chain workflows
5
+ */
6
+ import { Command } from "commander";
7
+ export declare const seatableCommand: Command;
8
+ export default seatableCommand;