@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
@@ -1,6 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  import { getConfig } from "./config.js";
3
- import { paginateList, printPaginated } from "../lib/pagination.js";
4
3
  export const ideaCommand = new Command("idea")
5
4
  .description("Manage ideas");
6
5
  // Helper: Ensure API is configured
@@ -12,22 +11,12 @@ function ensureConfig() {
12
11
  }
13
12
  return config;
14
13
  }
15
- // Status labels and icons
16
- const STATUS_CONFIG = {
17
- draft: { label: "Draft", icon: "πŸ“" },
18
- active: { label: "Active", icon: "πŸ’‘" },
19
- archived: { label: "Archived", icon: "πŸ“¦" },
20
- converted: { label: "Converted", icon: "βœ…" },
21
- };
22
14
  // husky idea list
23
15
  ideaCommand
24
16
  .command("list")
25
17
  .description("List all ideas")
26
18
  .option("--status <status>", "Filter by status (draft, active, archived, converted)")
27
19
  .option("--json", "Output as JSON")
28
- .option("-p, --page <num>", "Page number (starts at 1)", "1")
29
- .option("-n, --per-page <num>", "Items per page (default: all)")
30
- .option("-i, --interactive", "Interactive pagination with arrow keys")
31
20
  .action(async (options) => {
32
21
  const config = ensureConfig();
33
22
  try {
@@ -44,47 +33,10 @@ ideaCommand
44
33
  const ideas = await res.json();
45
34
  if (options.json) {
46
35
  console.log(JSON.stringify(ideas, null, 2));
47
- return;
48
36
  }
49
- // Interactive pagination mode
50
- if (options.interactive) {
51
- await paginateList({
52
- items: ideas,
53
- pageSize: 10,
54
- title: "Ideas",
55
- emptyMessage: "No ideas found.",
56
- renderItem: (idea) => {
57
- const cfg = STATUS_CONFIG[idea.status] || { label: idea.status, icon: "β—‹" };
58
- return ` ${cfg.icon} ${idea.title.slice(0, 40).padEnd(40)} β”‚ ${cfg.label}`;
59
- },
60
- selectableItems: true,
61
- onSelect: async (idea) => {
62
- console.clear();
63
- console.log(`\n Idea: ${idea.title}`);
64
- console.log(" " + "─".repeat(50));
65
- console.log(` ID: ${idea.id}`);
66
- console.log(` Status: ${STATUS_CONFIG[idea.status]?.label || idea.status}`);
67
- if (idea.category)
68
- console.log(` Category: ${idea.category}`);
69
- if (idea.description)
70
- console.log(` Desc: ${idea.description}`);
71
- console.log("");
72
- },
73
- });
74
- return;
75
- }
76
- // Simple pagination mode
77
- if (options.perPage) {
78
- const pageNum = parseInt(options.page, 10) - 1;
79
- const pageSize = parseInt(options.perPage, 10);
80
- printPaginated(ideas, pageNum, pageSize, (idea) => {
81
- const cfg = STATUS_CONFIG[idea.status] || { label: idea.status, icon: "β—‹" };
82
- return ` ${cfg.icon} ${idea.id.slice(0, 8)} β”‚ ${idea.title}`;
83
- }, "Ideas");
84
- return;
37
+ else {
38
+ printIdeas(ideas);
85
39
  }
86
- // Default list
87
- printIdeas(ideas);
88
40
  }
89
41
  catch (error) {
90
42
  console.error("Error fetching ideas:", error);
@@ -1,6 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  import { getConfig } from "./config.js";
3
- import { paginateList, printPaginated } from "../lib/pagination.js";
4
3
  export const projectCommand = new Command("project")
5
4
  .description("Manage projects and knowledge");
6
5
  // Helper: Ensure API is configured
@@ -38,9 +37,6 @@ projectCommand
38
37
  .option("--status <status>", "Filter by status (active, archived)")
39
38
  .option("--work-status <workStatus>", "Filter by work status (planning, in_progress, review, completed, on_hold)")
40
39
  .option("--archived", "Include archived projects")
41
- .option("-p, --page <num>", "Page number (starts at 1)", "1")
42
- .option("-n, --per-page <num>", "Items per page (default: all)")
43
- .option("-i, --interactive", "Interactive pagination with arrow keys")
44
40
  .action(async (options) => {
45
41
  const config = ensureConfig();
46
42
  try {
@@ -64,51 +60,10 @@ projectCommand
64
60
  }
65
61
  if (options.json) {
66
62
  console.log(JSON.stringify(projects, null, 2));
67
- return;
68
63
  }
69
- // Interactive pagination mode
70
- if (options.interactive) {
71
- await paginateList({
72
- items: projects,
73
- pageSize: 10,
74
- title: "Projects",
75
- emptyMessage: "No projects found.",
76
- renderItem: (project) => {
77
- const statusIcon = project.status === "archived" ? "πŸ“¦" : "πŸ“";
78
- const workStatusLabel = WORK_STATUS_LABELS[project.workStatus] || project.workStatus;
79
- return ` ${statusIcon} ${project.name.slice(0, 30).padEnd(30)} β”‚ ${workStatusLabel}`;
80
- },
81
- selectableItems: true,
82
- onSelect: async (project) => {
83
- console.clear();
84
- console.log(`\n Project: ${project.name}`);
85
- console.log(" " + "─".repeat(50));
86
- console.log(` ID: ${project.id}`);
87
- console.log(` Status: ${project.status}`);
88
- console.log(` Work: ${WORK_STATUS_LABELS[project.workStatus]}`);
89
- if (project.description)
90
- console.log(` Desc: ${project.description}`);
91
- if (project.techStack)
92
- console.log(` Tech: ${project.techStack}`);
93
- if (project.githubRepo)
94
- console.log(` GitHub: ${project.githubRepo}`);
95
- console.log("");
96
- },
97
- });
98
- return;
99
- }
100
- // Simple pagination mode
101
- if (options.perPage) {
102
- const pageNum = parseInt(options.page, 10) - 1;
103
- const pageSize = parseInt(options.perPage, 10);
104
- printPaginated(projects, pageNum, pageSize, (project) => {
105
- const statusIcon = project.status === "archived" ? "πŸ“¦" : "πŸ“";
106
- return ` ${statusIcon} ${project.id.slice(0, 8)} β”‚ ${project.name}`;
107
- }, "Projects");
108
- return;
64
+ else {
65
+ printProjects(projects);
109
66
  }
110
- // Default: standard list
111
- printProjects(projects);
112
67
  }
113
68
  catch (error) {
114
69
  console.error("Error fetching projects:", error);
@@ -152,113 +152,6 @@ roadmapCommand
152
152
  process.exit(1);
153
153
  }
154
154
  });
155
- // husky roadmap update-phase <roadmapId> <phaseId>
156
- roadmapCommand
157
- .command("update-phase <roadmapId> <phaseId>")
158
- .description("Update a roadmap phase")
159
- .option("-n, --name <name>", "New phase name")
160
- .option("-d, --description <description>", "New phase description")
161
- .option("--status <status>", "Phase status (planned, in_progress, completed)")
162
- .option("--order <order>", "Phase order (0-based)")
163
- .action(async (roadmapId, phaseId, options) => {
164
- const config = ensureConfig();
165
- // Build update payload
166
- const updateData = { phaseId };
167
- if (options.name)
168
- updateData.name = options.name;
169
- if (options.description)
170
- updateData.description = options.description;
171
- if (options.status) {
172
- const validStatuses = ["planned", "in_progress", "completed"];
173
- if (!validStatuses.includes(options.status)) {
174
- console.error(`Error: Invalid status "${options.status}". Must be one of: ${validStatuses.join(", ")}`);
175
- process.exit(1);
176
- }
177
- updateData.status = options.status;
178
- }
179
- if (options.order !== undefined) {
180
- const order = parseInt(options.order, 10);
181
- if (isNaN(order) || order < 0) {
182
- console.error("Error: Order must be a non-negative integer");
183
- process.exit(1);
184
- }
185
- updateData.order = order;
186
- }
187
- if (Object.keys(updateData).length === 1) {
188
- console.error("Error: No update options provided. Use -n/--name, -d/--description, --status, or --order");
189
- process.exit(1);
190
- }
191
- try {
192
- const res = await fetch(`${config.apiUrl}/api/roadmaps/${roadmapId}/phases`, {
193
- method: "PATCH",
194
- headers: {
195
- "Content-Type": "application/json",
196
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
197
- },
198
- body: JSON.stringify(updateData),
199
- });
200
- if (!res.ok) {
201
- if (res.status === 404) {
202
- console.error(`Error: Roadmap or phase not found`);
203
- }
204
- else {
205
- const errorBody = await res.json().catch(() => ({}));
206
- console.error(`Error: API returned ${res.status}`, errorBody.error || "");
207
- }
208
- process.exit(1);
209
- }
210
- const roadmap = await res.json();
211
- const updatedPhase = roadmap.phases.find((p) => p.id === phaseId);
212
- console.log(`βœ“ Phase updated successfully`);
213
- if (updatedPhase) {
214
- console.log(` Name: ${updatedPhase.name}`);
215
- console.log(` Status: ${updatedPhase.status}`);
216
- console.log(` Order: ${updatedPhase.order}`);
217
- }
218
- }
219
- catch (error) {
220
- console.error("Error updating phase:", error);
221
- process.exit(1);
222
- }
223
- });
224
- // husky roadmap delete-phase <roadmapId> <phaseId>
225
- roadmapCommand
226
- .command("delete-phase <roadmapId> <phaseId>")
227
- .description("Delete a phase from a roadmap (including all its features)")
228
- .option("--force", "Skip confirmation")
229
- .action(async (roadmapId, phaseId, options) => {
230
- const config = ensureConfig();
231
- if (!options.force) {
232
- console.log("Warning: This will permanently delete the phase and all its features.");
233
- console.log("Use --force to confirm deletion.");
234
- process.exit(1);
235
- }
236
- try {
237
- const url = new URL(`/api/roadmaps/${roadmapId}/phases`, config.apiUrl);
238
- url.searchParams.set("phaseId", phaseId);
239
- const res = await fetch(url.toString(), {
240
- method: "DELETE",
241
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
242
- });
243
- if (!res.ok) {
244
- if (res.status === 404) {
245
- console.error(`Error: Roadmap or phase not found`);
246
- }
247
- else {
248
- const errorBody = await res.json().catch(() => ({}));
249
- console.error(`Error: API returned ${res.status}`, errorBody.error || "");
250
- }
251
- process.exit(1);
252
- }
253
- const roadmap = await res.json();
254
- console.log(`βœ“ Phase deleted`);
255
- console.log(` Remaining phases: ${roadmap.phases.length}`);
256
- }
257
- catch (error) {
258
- console.error("Error deleting phase:", error);
259
- process.exit(1);
260
- }
261
- });
262
155
  // husky roadmap add-feature <roadmapId> <phaseId> <title>
263
156
  roadmapCommand
264
157
  .command("add-feature <roadmapId> <phaseId> <title>")
@@ -182,7 +182,7 @@ taskCommand
182
182
  renderItem: (task) => {
183
183
  const statusIcon = task.status === "done" ? "βœ“" : task.status === "in_progress" ? "β–Ά" : "β—‹";
184
184
  const priorityIcon = task.priority === "urgent" ? "πŸ”΄" : task.priority === "high" ? "🟠" : "";
185
- return ` ${statusIcon} ${task.id.slice(0, 8)} β”‚ ${task.title.slice(0, 40).padEnd(40)} ${priorityIcon}`;
185
+ return ` ${statusIcon} ${task.id.padEnd(20)} β”‚ ${task.title.slice(0, 40).padEnd(40)} ${priorityIcon}`;
186
186
  },
187
187
  selectableItems: true,
188
188
  onSelect: async (task) => {
@@ -205,7 +205,7 @@ taskCommand
205
205
  const pageSize = parseInt(options.perPage, 10);
206
206
  printPaginated(tasks, pageNum, pageSize, (task) => {
207
207
  const statusIcon = task.status === "done" ? "βœ“" : task.status === "in_progress" ? "β–Ά" : "β—‹";
208
- return ` ${statusIcon} ${task.id.slice(0, 8)} β”‚ ${task.title}`;
208
+ return ` ${statusIcon} ${task.id.padEnd(20)} β”‚ ${task.title}`;
209
209
  }, "Tasks");
210
210
  return;
211
211
  }
@@ -545,19 +545,13 @@ taskCommand
545
545
  process.exit(1);
546
546
  }
547
547
  });
548
- // husky task message [--id <id>] -m <text>
548
+ // husky task message <id> "message" - post status message to task
549
549
  taskCommand
550
- .command("message")
550
+ .command("message <id> <message>")
551
551
  .description("Post a status message to a task")
552
- .option("--id <id>", "Task ID (or use HUSKY_TASK_ID env var)")
553
- .option("-m, --message <text>", "Status message", "")
554
- .action(async (options) => {
552
+ .action(async (id, message) => {
555
553
  const config = ensureConfig();
556
- const taskId = getTaskId(options);
557
- if (!options.message) {
558
- console.error("Error: Message required. Use -m or --message");
559
- process.exit(1);
560
- }
554
+ const taskId = id;
561
555
  try {
562
556
  const res = await fetch(`${config.apiUrl}/api/tasks/${taskId}/status`, {
563
557
  method: "POST",
@@ -566,7 +560,7 @@ taskCommand
566
560
  ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
567
561
  },
568
562
  body: JSON.stringify({
569
- message: options.message,
563
+ message,
570
564
  timestamp: new Date().toISOString(),
571
565
  }),
572
566
  });
@@ -1079,15 +1073,15 @@ function printTasks(tasks) {
1079
1073
  if (statusTasks.length === 0)
1080
1074
  continue;
1081
1075
  console.log(`\n ${label}`);
1082
- console.log(" " + "─".repeat(85));
1083
- console.log(` ${"ID".padEnd(10)} ${"Title".padEnd(30)} ${"Project".padEnd(15)} ${"Project ID".padEnd(12)} ${"Priority".padEnd(6)}`);
1084
- console.log(" " + "─".repeat(85));
1076
+ console.log(" " + "─".repeat(95));
1077
+ console.log(` ${"ID".padEnd(20)} ${"Title".padEnd(30)} ${"Project".padEnd(15)} ${"Project ID".padEnd(12)} ${"Priority".padEnd(6)}`);
1078
+ console.log(" " + "─".repeat(95));
1085
1079
  for (const task of statusTasks) {
1086
1080
  const agentStr = task.agent ? ` (${task.agent})` : "";
1087
1081
  const doneStr = status === "done" ? " βœ“" : "";
1088
1082
  const projectName = task.projectName?.slice(0, 15).padEnd(15) || "β€”".padEnd(15);
1089
1083
  const projectId = task.projectId?.slice(0, 12).padEnd(12) || "β€”".padEnd(12);
1090
- console.log(` ${task.id.slice(0, 10).padEnd(10)} ${task.title.slice(0, 30).padEnd(30)} ${projectName} ${projectId} ${task.priority.padEnd(6)}${agentStr}${doneStr}`);
1084
+ console.log(` ${task.id.padEnd(20)} ${task.title.slice(0, 30).padEnd(30)} ${projectName} ${projectId} ${task.priority.padEnd(6)}${agentStr}${doneStr}`);
1091
1085
  }
1092
1086
  }
1093
1087
  console.log("");
@@ -569,13 +569,6 @@ function printVMSessionDetail(session) {
569
569
  if (session.workflowId) {
570
570
  console.log(` Workflow ID: ${session.workflowId}`);
571
571
  }
572
- // Pool info
573
- if (session.sessionType) {
574
- console.log(` Session Type: ${session.sessionType === 'pool' ? 'Pool VM' : 'On-Demand'}`);
575
- if (session.poolVmId) {
576
- console.log(` Pool VM ID: ${session.poolVmId}`);
577
- }
578
- }
579
572
  console.log(`\n Prompt:`);
580
573
  console.log(` ${session.prompt}`);
581
574
  if (session.costEstimate !== undefined) {
@@ -626,221 +619,3 @@ function printLog(log) {
626
619
  }
627
620
  console.log(`${prefix}${timestamp} ${level} ${source} ${log.message}`);
628
621
  }
629
- // ============================================
630
- // VM POOL COMMANDS
631
- // ============================================
632
- const poolCommand = new Command("pool")
633
- .description("Manage VM pool (admin)");
634
- // husky vm pool status
635
- poolCommand
636
- .command("status")
637
- .description("Show VM pool status")
638
- .option("--json", "Output as JSON")
639
- .action(async (options) => {
640
- const config = ensureConfig();
641
- try {
642
- const res = await fetch(`${config.apiUrl}/api/admin/vm-pool`, {
643
- headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
644
- });
645
- if (!res.ok) {
646
- const errorData = await res.json().catch(() => ({}));
647
- throw new Error(errorData.error || `API error: ${res.status}`);
648
- }
649
- const data = await res.json();
650
- const vms = data.vms || [];
651
- if (options.json) {
652
- console.log(JSON.stringify(data, null, 2));
653
- }
654
- else {
655
- printPoolStatus(vms);
656
- }
657
- }
658
- catch (error) {
659
- console.error("Error fetching VM pool status:", error);
660
- process.exit(1);
661
- }
662
- });
663
- // husky vm pool init
664
- poolCommand
665
- .command("init")
666
- .description("Initialize VM pool (1 HOT + 4 COLD VMs)")
667
- .option("--json", "Output as JSON")
668
- .action(async (options) => {
669
- const config = ensureConfig();
670
- console.log("Initializing VM pool...");
671
- console.log("This will create 1 HOT VM + 4 COLD VMs\n");
672
- try {
673
- const res = await fetch(`${config.apiUrl}/api/admin/vm-pool/initialize`, {
674
- method: "POST",
675
- headers: {
676
- "Content-Type": "application/json",
677
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
678
- },
679
- });
680
- if (!res.ok) {
681
- const errorData = await res.json().catch(() => ({}));
682
- throw new Error(errorData.error || `API error: ${res.status}`);
683
- }
684
- const data = await res.json();
685
- if (options.json) {
686
- console.log(JSON.stringify(data, null, 2));
687
- }
688
- else {
689
- console.log(`VM Pool initialized!`);
690
- console.log(` HOT VMs: ${data.hotCount || 1}`);
691
- console.log(` COLD VMs: ${data.coldCount || 4}`);
692
- console.log(`\nRun 'husky vm pool status' to see pool details`);
693
- }
694
- }
695
- catch (error) {
696
- console.error("Error initializing VM pool:", error);
697
- process.exit(1);
698
- }
699
- });
700
- // husky vm pool suspend <id>
701
- poolCommand
702
- .command("suspend <id>")
703
- .description("Suspend a pool VM")
704
- .option("--json", "Output as JSON")
705
- .action(async (id, options) => {
706
- const config = ensureConfig();
707
- console.log(`Suspending VM ${id}...\n`);
708
- try {
709
- const res = await fetch(`${config.apiUrl}/api/admin/vm-pool/${id}/suspend`, {
710
- method: "POST",
711
- headers: {
712
- "Content-Type": "application/json",
713
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
714
- },
715
- });
716
- if (!res.ok) {
717
- const errorData = await res.json().catch(() => ({}));
718
- throw new Error(errorData.error || `API error: ${res.status}`);
719
- }
720
- const data = await res.json();
721
- if (options.json) {
722
- console.log(JSON.stringify(data, null, 2));
723
- }
724
- else {
725
- console.log(`VM suspended successfully`);
726
- console.log(` VM Name: ${data.vmName}`);
727
- }
728
- }
729
- catch (error) {
730
- console.error("Error suspending VM:", error);
731
- process.exit(1);
732
- }
733
- });
734
- // husky vm pool resume <id>
735
- poolCommand
736
- .command("resume <id>")
737
- .description("Resume a suspended pool VM")
738
- .option("--json", "Output as JSON")
739
- .action(async (id, options) => {
740
- const config = ensureConfig();
741
- console.log(`Resuming VM ${id}...\n`);
742
- try {
743
- const res = await fetch(`${config.apiUrl}/api/admin/vm-pool/${id}/resume`, {
744
- method: "POST",
745
- headers: {
746
- "Content-Type": "application/json",
747
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
748
- },
749
- });
750
- if (!res.ok) {
751
- const errorData = await res.json().catch(() => ({}));
752
- throw new Error(errorData.error || `API error: ${res.status}`);
753
- }
754
- const data = await res.json();
755
- if (options.json) {
756
- console.log(JSON.stringify(data, null, 2));
757
- }
758
- else {
759
- console.log(`VM resumed successfully`);
760
- console.log(` VM Name: ${data.vmName}`);
761
- }
762
- }
763
- catch (error) {
764
- console.error("Error resuming VM:", error);
765
- process.exit(1);
766
- }
767
- });
768
- // husky vm pool setup <id>
769
- poolCommand
770
- .command("setup <id>")
771
- .description("Start Claude subscription setup for a pool VM")
772
- .option("--json", "Output as JSON")
773
- .action(async (id, options) => {
774
- const config = ensureConfig();
775
- console.log(`Starting subscription setup for VM ${id}...\n`);
776
- try {
777
- const res = await fetch(`${config.apiUrl}/api/admin/vm-pool/${id}/setup`, {
778
- method: "POST",
779
- headers: {
780
- "Content-Type": "application/json",
781
- ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
782
- },
783
- });
784
- if (!res.ok) {
785
- const errorData = await res.json().catch(() => ({}));
786
- throw new Error(errorData.error || `API error: ${res.status}`);
787
- }
788
- const data = await res.json();
789
- if (options.json) {
790
- console.log(JSON.stringify(data, null, 2));
791
- }
792
- else {
793
- console.log(`Setup session created!`);
794
- console.log(` Session ID: ${data.sessionId}`);
795
- console.log(` VM Name: ${data.vmName}`);
796
- console.log(` VM IP: ${data.vmIpAddress || "pending"}`);
797
- console.log(`\nInstructions:`);
798
- for (const instruction of data.instructions || []) {
799
- console.log(` ${instruction}`);
800
- }
801
- console.log(`\nExpires: ${data.expiresAt}`);
802
- }
803
- }
804
- catch (error) {
805
- console.error("Error starting setup:", error);
806
- process.exit(1);
807
- }
808
- });
809
- // Print helper for pool status
810
- function printPoolStatus(vms) {
811
- if (vms.length === 0) {
812
- console.log("\n VM Pool is empty.");
813
- console.log(" Initialize with: husky vm pool init\n");
814
- return;
815
- }
816
- // Calculate stats
817
- const total = vms.length;
818
- const available = vms.filter(v => v.status === 'available').length;
819
- const inUse = vms.filter(v => v.status === 'in_use').length;
820
- const suspended = vms.filter(v => v.status === 'suspended').length;
821
- const hotVms = vms.filter(v => v.tier === 'hot').length;
822
- const authenticated = vms.filter(v => v.subscriptionAuth).length;
823
- // Estimate monthly cost
824
- const runningCost = (available + inUse) * 12.24; // $12.24/month for e2-small running
825
- const suspendedCost = suspended * 0.30; // $0.30/month for suspended
826
- const totalCost = runningCost + suspendedCost;
827
- console.log("\n VM POOL STATUS");
828
- console.log(" " + "=".repeat(70));
829
- console.log(` Total: ${total} | Available: ${available} | In Use: ${inUse} | Suspended: ${suspended}`);
830
- console.log(` HOT VMs: ${hotVms} | Authenticated: ${authenticated}/${total}`);
831
- console.log(` Est. Monthly Cost: ~$${totalCost.toFixed(2)}`);
832
- console.log(" " + "-".repeat(70));
833
- console.log(` ${"ID".padEnd(24)} ${"NAME".padEnd(18)} ${"STATUS".padEnd(12)} ${"TIER".padEnd(6)} ${"AUTH".padEnd(5)} LAST USED`);
834
- console.log(" " + "-".repeat(70));
835
- for (const vm of vms) {
836
- const status = vm.status.toUpperCase().padEnd(12);
837
- const tier = vm.tier.toUpperCase().padEnd(6);
838
- const auth = vm.subscriptionAuth ? "Yes" : "No";
839
- const lastUsed = new Date(vm.lastUsedAt).toLocaleDateString();
840
- const truncatedName = vm.vmName.length > 16 ? vm.vmName.substring(0, 13) + "..." : vm.vmName;
841
- console.log(` ${vm.id.padEnd(24)} ${truncatedName.padEnd(18)} ${status} ${tier} ${auth.padEnd(5)} ${lastUsed}`);
842
- }
843
- console.log("");
844
- }
845
- // Add pool command to vm command
846
- vmCommand.addCommand(poolCommand);
@@ -1,6 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  import { getConfig } from "./config.js";
3
- import { paginateList, printPaginated } from "../lib/pagination.js";
4
3
  export const workflowCommand = new Command("workflow")
5
4
  .description("Manage workflows and workflow steps");
6
5
  // Helper: Ensure API is configured
@@ -12,28 +11,12 @@ function ensureConfig() {
12
11
  }
13
12
  return config;
14
13
  }
15
- // Status icons
16
- const STATUS_ICONS = {
17
- draft: "πŸ“",
18
- active: "▢️",
19
- paused: "⏸️",
20
- archived: "πŸ“¦",
21
- };
22
- // Action labels
23
- const ACTION_LABELS = {
24
- manual: "Manual",
25
- semi_automated: "Semi-Auto",
26
- fully_automated: "Auto",
27
- };
28
14
  // husky workflow list
29
15
  workflowCommand
30
16
  .command("list")
31
17
  .description("List all workflows")
32
18
  .option("--value-stream <valueStream>", "Filter by value stream")
33
19
  .option("--json", "Output as JSON")
34
- .option("-p, --page <num>", "Page number (starts at 1)", "1")
35
- .option("-n, --per-page <num>", "Items per page (default: all)")
36
- .option("-i, --interactive", "Interactive pagination with arrow keys")
37
20
  .action(async (options) => {
38
21
  const config = ensureConfig();
39
22
  try {
@@ -50,49 +33,10 @@ workflowCommand
50
33
  const workflows = await res.json();
51
34
  if (options.json) {
52
35
  console.log(JSON.stringify(workflows, null, 2));
53
- return;
54
- }
55
- // Interactive pagination mode
56
- if (options.interactive) {
57
- await paginateList({
58
- items: workflows,
59
- pageSize: 10,
60
- title: "Workflows",
61
- emptyMessage: "No workflows found.",
62
- renderItem: (wf) => {
63
- const icon = STATUS_ICONS[wf.status] || "β—‹";
64
- const action = ACTION_LABELS[wf.action] || wf.action;
65
- return ` ${icon} ${wf.name.slice(0, 35).padEnd(35)} β”‚ ${action}`;
66
- },
67
- selectableItems: true,
68
- onSelect: async (wf) => {
69
- console.clear();
70
- console.log(`\n Workflow: ${wf.name}`);
71
- console.log(" " + "─".repeat(50));
72
- console.log(` ID: ${wf.id}`);
73
- console.log(` Status: ${wf.status}`);
74
- console.log(` Action: ${ACTION_LABELS[wf.action]}`);
75
- console.log(` ValueStream: ${wf.valueStream}`);
76
- console.log(` Autonomy: ${wf.autonomyWeight}%`);
77
- if (wf.description)
78
- console.log(` Desc: ${wf.description}`);
79
- console.log("");
80
- },
81
- });
82
- return;
83
- }
84
- // Simple pagination mode
85
- if (options.perPage) {
86
- const pageNum = parseInt(options.page, 10) - 1;
87
- const pageSize = parseInt(options.perPage, 10);
88
- printPaginated(workflows, pageNum, pageSize, (wf) => {
89
- const icon = STATUS_ICONS[wf.status] || "β—‹";
90
- return ` ${icon} ${wf.id.slice(0, 8)} β”‚ ${wf.name}`;
91
- }, "Workflows");
92
- return;
93
- }
94
- // Default list
95
- printWorkflows(workflows);
36
+ }
37
+ else {
38
+ printWorkflows(workflows);
39
+ }
96
40
  }
97
41
  catch (error) {
98
42
  console.error("Error fetching workflows:", error);