@simonfestl/husky-cli 0.5.1 → 0.6.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 (37) hide show
  1. package/dist/commands/config.js +4 -3
  2. package/dist/commands/idea.js +9 -7
  3. package/dist/commands/interactive/changelog.d.ts +1 -0
  4. package/dist/commands/interactive/changelog.js +398 -0
  5. package/dist/commands/interactive/departments.d.ts +1 -0
  6. package/dist/commands/interactive/departments.js +242 -0
  7. package/dist/commands/interactive/ideas.d.ts +1 -0
  8. package/dist/commands/interactive/ideas.js +311 -0
  9. package/dist/commands/interactive/jules-sessions.d.ts +1 -0
  10. package/dist/commands/interactive/jules-sessions.js +460 -0
  11. package/dist/commands/interactive/processes.d.ts +1 -0
  12. package/dist/commands/interactive/processes.js +271 -0
  13. package/dist/commands/interactive/projects.d.ts +1 -0
  14. package/dist/commands/interactive/projects.js +297 -0
  15. package/dist/commands/interactive/roadmaps.d.ts +1 -0
  16. package/dist/commands/interactive/roadmaps.js +650 -0
  17. package/dist/commands/interactive/strategy.d.ts +1 -0
  18. package/dist/commands/interactive/strategy.js +790 -0
  19. package/dist/commands/interactive/tasks.d.ts +1 -0
  20. package/dist/commands/interactive/tasks.js +415 -0
  21. package/dist/commands/interactive/utils.d.ts +15 -0
  22. package/dist/commands/interactive/utils.js +54 -0
  23. package/dist/commands/interactive/vm-sessions.d.ts +1 -0
  24. package/dist/commands/interactive/vm-sessions.js +319 -0
  25. package/dist/commands/interactive/workflows.d.ts +1 -0
  26. package/dist/commands/interactive/workflows.js +442 -0
  27. package/dist/commands/interactive/worktrees.d.ts +6 -0
  28. package/dist/commands/interactive/worktrees.js +354 -0
  29. package/dist/commands/interactive.js +118 -1208
  30. package/dist/commands/worktree.d.ts +2 -0
  31. package/dist/commands/worktree.js +404 -0
  32. package/dist/index.js +3 -1
  33. package/dist/lib/merge-lock.d.ts +83 -0
  34. package/dist/lib/merge-lock.js +242 -0
  35. package/dist/lib/worktree.d.ts +133 -0
  36. package/dist/lib/worktree.js +473 -0
  37. package/package.json +1 -1
@@ -0,0 +1,442 @@
1
+ import { select, input, confirm } from "@inquirer/prompts";
2
+ import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
+ export async function workflowsMenu() {
4
+ const config = ensureConfig();
5
+ const menuItems = [
6
+ { name: "List all workflows", value: "list" },
7
+ { name: "View workflow details", value: "view" },
8
+ { name: "Create new workflow", value: "create" },
9
+ { name: "Update workflow", value: "update" },
10
+ { name: "Delete workflow", value: "delete" },
11
+ { name: "Manage steps", value: "steps" },
12
+ { name: "Generate steps with AI", value: "generate" },
13
+ { name: "Back to main menu", value: "back" },
14
+ ];
15
+ const choice = await select({
16
+ message: "Workflows:",
17
+ choices: menuItems,
18
+ });
19
+ switch (choice) {
20
+ case "list":
21
+ await listWorkflows(config);
22
+ break;
23
+ case "view":
24
+ await viewWorkflow(config);
25
+ break;
26
+ case "create":
27
+ await createWorkflow(config);
28
+ break;
29
+ case "update":
30
+ await updateWorkflow(config);
31
+ break;
32
+ case "delete":
33
+ await deleteWorkflow(config);
34
+ break;
35
+ case "steps":
36
+ await manageSteps(config);
37
+ break;
38
+ case "generate":
39
+ await generateSteps(config);
40
+ break;
41
+ case "back":
42
+ return;
43
+ }
44
+ }
45
+ async function fetchWorkflows(config) {
46
+ const res = await fetch(`${config.apiUrl}/api/workflows`, {
47
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
48
+ });
49
+ if (!res.ok)
50
+ throw new Error(`API returned ${res.status}`);
51
+ return res.json();
52
+ }
53
+ async function selectWorkflow(config, message) {
54
+ const workflows = await fetchWorkflows(config);
55
+ if (workflows.length === 0) {
56
+ console.log("\n No workflows found.\n");
57
+ await pressEnterToContinue();
58
+ return null;
59
+ }
60
+ const choices = workflows.map((w) => ({
61
+ name: `[${w.status}] ${truncate(w.name, 35)} (${w.valueStream.replace(/_/g, " ")})`,
62
+ value: w.id,
63
+ }));
64
+ choices.push({ name: "Cancel", value: "__cancel__" });
65
+ const workflowId = await select({ message, choices });
66
+ if (workflowId === "__cancel__")
67
+ return null;
68
+ return workflows.find((w) => w.id === workflowId) || null;
69
+ }
70
+ async function listWorkflows(config) {
71
+ try {
72
+ const workflows = await fetchWorkflows(config);
73
+ console.log("\n WORKFLOWS");
74
+ console.log(" " + "-".repeat(70));
75
+ if (workflows.length === 0) {
76
+ console.log(" No workflows found.");
77
+ }
78
+ else {
79
+ for (const wf of workflows) {
80
+ console.log(` [${wf.status}] ${truncate(wf.name, 40).padEnd(40)} ${wf.action}`);
81
+ console.log(` ID: ${wf.id}`);
82
+ console.log(` Value Stream: ${wf.valueStream.replace(/_/g, " ")}`);
83
+ }
84
+ }
85
+ console.log("");
86
+ await pressEnterToContinue();
87
+ }
88
+ catch (error) {
89
+ console.error("\n Error fetching workflows:", error);
90
+ await pressEnterToContinue();
91
+ }
92
+ }
93
+ async function viewWorkflow(config) {
94
+ try {
95
+ const wf = await selectWorkflow(config, "Select workflow to view:");
96
+ if (!wf)
97
+ return;
98
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
99
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
100
+ });
101
+ if (!res.ok) {
102
+ console.error(`\n Error: API returned ${res.status}\n`);
103
+ await pressEnterToContinue();
104
+ return;
105
+ }
106
+ const fullWf = await res.json();
107
+ console.log(`\n Workflow: ${fullWf.name}`);
108
+ console.log(" " + "-".repeat(50));
109
+ console.log(` ID: ${fullWf.id}`);
110
+ console.log(` Status: ${fullWf.status}`);
111
+ console.log(` Action: ${fullWf.action}`);
112
+ console.log(` Value Stream: ${fullWf.valueStream.replace(/_/g, " ")}`);
113
+ if (fullWf.description) {
114
+ console.log(` Description: ${fullWf.description}`);
115
+ }
116
+ if (fullWf.steps && fullWf.steps.length > 0) {
117
+ console.log(`\n Steps (${fullWf.steps.length}):`);
118
+ for (const step of fullWf.steps) {
119
+ console.log(` ${step.order + 1}. ${step.name}`);
120
+ }
121
+ }
122
+ console.log("");
123
+ await pressEnterToContinue();
124
+ }
125
+ catch (error) {
126
+ console.error("\n Error viewing workflow:", error);
127
+ await pressEnterToContinue();
128
+ }
129
+ }
130
+ async function createWorkflow(config) {
131
+ try {
132
+ const name = await input({
133
+ message: "Workflow name:",
134
+ validate: (value) => (value.length > 0 ? true : "Name is required"),
135
+ });
136
+ const description = await input({
137
+ message: "Description (optional):",
138
+ });
139
+ const valueStream = await select({
140
+ message: "Value stream:",
141
+ choices: [
142
+ { name: "Order to Delivery", value: "order_to_delivery" },
143
+ { name: "Procure to Pay", value: "procure_to_pay" },
144
+ { name: "Returns Management", value: "returns_management" },
145
+ { name: "Product Lifecycle", value: "product_lifecycle" },
146
+ { name: "Customer Service", value: "customer_service" },
147
+ { name: "Finance Admin", value: "finance_admin" },
148
+ ],
149
+ default: "customer_service",
150
+ });
151
+ const status = await select({
152
+ message: "Status:",
153
+ choices: [
154
+ { name: "Owner Only", value: "owner_only" },
155
+ { name: "Approval Needed", value: "approval_needed" },
156
+ { name: "Supervised", value: "supervised" },
157
+ { name: "Delegated", value: "delegated" },
158
+ { name: "Automated", value: "automated" },
159
+ ],
160
+ default: "owner_only",
161
+ });
162
+ const action = await select({
163
+ message: "Action:",
164
+ choices: [
165
+ { name: "Document", value: "document" },
166
+ { name: "Automate", value: "automate" },
167
+ { name: "Hire", value: "hire" },
168
+ { name: "Fix", value: "fix" },
169
+ { name: "OK", value: "ok" },
170
+ ],
171
+ default: "document",
172
+ });
173
+ const res = await fetch(`${config.apiUrl}/api/workflows`, {
174
+ method: "POST",
175
+ headers: {
176
+ "Content-Type": "application/json",
177
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
178
+ },
179
+ body: JSON.stringify({
180
+ name,
181
+ description: description || "",
182
+ valueStream,
183
+ status,
184
+ action,
185
+ }),
186
+ });
187
+ if (!res.ok) {
188
+ console.error(`\n Error: API returned ${res.status}\n`);
189
+ await pressEnterToContinue();
190
+ return;
191
+ }
192
+ const wf = await res.json();
193
+ console.log(`\n ✓ Workflow created!`);
194
+ console.log(` ID: ${wf.id}`);
195
+ console.log(` Name: ${wf.name}\n`);
196
+ await pressEnterToContinue();
197
+ }
198
+ catch (error) {
199
+ console.error("\n Error creating workflow:", error);
200
+ await pressEnterToContinue();
201
+ }
202
+ }
203
+ async function updateWorkflow(config) {
204
+ try {
205
+ const wf = await selectWorkflow(config, "Select workflow to update:");
206
+ if (!wf)
207
+ return;
208
+ const updateChoices = [
209
+ { name: "Name", value: "name" },
210
+ { name: "Description", value: "description" },
211
+ { name: "Status", value: "status" },
212
+ { name: "Action", value: "action" },
213
+ { name: "Cancel", value: "cancel" },
214
+ ];
215
+ const field = await select({ message: "What to update?", choices: updateChoices });
216
+ if (field === "cancel")
217
+ return;
218
+ let updateData = {};
219
+ switch (field) {
220
+ case "name":
221
+ updateData.name = await input({
222
+ message: "New name:",
223
+ default: wf.name,
224
+ validate: (v) => (v.length > 0 ? true : "Name required"),
225
+ });
226
+ break;
227
+ case "description":
228
+ updateData.description = await input({
229
+ message: "New description:",
230
+ default: wf.description || "",
231
+ });
232
+ break;
233
+ case "status":
234
+ updateData.status = await select({
235
+ message: "New status:",
236
+ choices: [
237
+ { name: "Owner Only", value: "owner_only" },
238
+ { name: "Approval Needed", value: "approval_needed" },
239
+ { name: "Supervised", value: "supervised" },
240
+ { name: "Delegated", value: "delegated" },
241
+ { name: "Automated", value: "automated" },
242
+ ],
243
+ default: wf.status,
244
+ });
245
+ break;
246
+ case "action":
247
+ updateData.action = await select({
248
+ message: "New action:",
249
+ choices: [
250
+ { name: "Document", value: "document" },
251
+ { name: "Automate", value: "automate" },
252
+ { name: "Hire", value: "hire" },
253
+ { name: "Fix", value: "fix" },
254
+ { name: "OK", value: "ok" },
255
+ ],
256
+ default: wf.action,
257
+ });
258
+ break;
259
+ }
260
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
261
+ method: "PATCH",
262
+ headers: {
263
+ "Content-Type": "application/json",
264
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
265
+ },
266
+ body: JSON.stringify(updateData),
267
+ });
268
+ if (!res.ok) {
269
+ console.error(`\n Error: API returned ${res.status}\n`);
270
+ await pressEnterToContinue();
271
+ return;
272
+ }
273
+ console.log(`\n ✓ Workflow updated!\n`);
274
+ await pressEnterToContinue();
275
+ }
276
+ catch (error) {
277
+ console.error("\n Error updating workflow:", error);
278
+ await pressEnterToContinue();
279
+ }
280
+ }
281
+ async function deleteWorkflow(config) {
282
+ try {
283
+ const wf = await selectWorkflow(config, "Select workflow to delete:");
284
+ if (!wf)
285
+ return;
286
+ const confirmed = await confirm({
287
+ message: `Delete "${wf.name}"? This cannot be undone.`,
288
+ default: false,
289
+ });
290
+ if (!confirmed) {
291
+ console.log("\n Cancelled.\n");
292
+ await pressEnterToContinue();
293
+ return;
294
+ }
295
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
296
+ method: "DELETE",
297
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
298
+ });
299
+ if (!res.ok) {
300
+ console.error(`\n Error: API returned ${res.status}\n`);
301
+ await pressEnterToContinue();
302
+ return;
303
+ }
304
+ console.log(`\n ✓ Workflow deleted.\n`);
305
+ await pressEnterToContinue();
306
+ }
307
+ catch (error) {
308
+ console.error("\n Error deleting workflow:", error);
309
+ await pressEnterToContinue();
310
+ }
311
+ }
312
+ async function manageSteps(config) {
313
+ try {
314
+ const wf = await selectWorkflow(config, "Select workflow:");
315
+ if (!wf)
316
+ return;
317
+ const stepChoices = [
318
+ { name: "List steps", value: "list" },
319
+ { name: "Add step", value: "add" },
320
+ { name: "Delete step", value: "delete" },
321
+ { name: "Back", value: "back" },
322
+ ];
323
+ const action = await select({ message: "Step action:", choices: stepChoices });
324
+ if (action === "back")
325
+ return;
326
+ if (action === "list") {
327
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
328
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
329
+ });
330
+ const fullWf = await res.json();
331
+ console.log(`\n Steps for: ${fullWf.name}`);
332
+ console.log(" " + "-".repeat(50));
333
+ if (!fullWf.steps || fullWf.steps.length === 0) {
334
+ console.log(" No steps defined.");
335
+ }
336
+ else {
337
+ for (const step of fullWf.steps) {
338
+ console.log(` ${step.order + 1}. ${step.name}`);
339
+ if (step.description) {
340
+ console.log(` ${step.description}`);
341
+ }
342
+ }
343
+ }
344
+ console.log("");
345
+ await pressEnterToContinue();
346
+ }
347
+ else if (action === "add") {
348
+ const name = await input({
349
+ message: "Step name:",
350
+ validate: (v) => (v.length > 0 ? true : "Name required"),
351
+ });
352
+ const description = await input({
353
+ message: "Step description (optional):",
354
+ });
355
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}/steps`, {
356
+ method: "POST",
357
+ headers: {
358
+ "Content-Type": "application/json",
359
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
360
+ },
361
+ body: JSON.stringify({ name, description: description || undefined }),
362
+ });
363
+ if (!res.ok) {
364
+ console.error(`\n Error: API returned ${res.status}\n`);
365
+ }
366
+ else {
367
+ console.log(`\n ✓ Step added!\n`);
368
+ }
369
+ await pressEnterToContinue();
370
+ }
371
+ else if (action === "delete") {
372
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}`, {
373
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
374
+ });
375
+ const fullWf = await res.json();
376
+ if (!fullWf.steps || fullWf.steps.length === 0) {
377
+ console.log("\n No steps to delete.\n");
378
+ await pressEnterToContinue();
379
+ return;
380
+ }
381
+ const stepChoices = fullWf.steps.map((s) => ({
382
+ name: `${s.order + 1}. ${s.name}`,
383
+ value: s.id,
384
+ }));
385
+ stepChoices.push({ name: "Cancel", value: "__cancel__" });
386
+ const stepId = await select({ message: "Select step to delete:", choices: stepChoices });
387
+ if (stepId === "__cancel__")
388
+ return;
389
+ const delRes = await fetch(`${config.apiUrl}/api/workflows/${wf.id}/steps/${stepId}`, {
390
+ method: "DELETE",
391
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
392
+ });
393
+ if (!delRes.ok) {
394
+ console.error(`\n Error: API returned ${delRes.status}\n`);
395
+ }
396
+ else {
397
+ console.log(`\n ✓ Step deleted.\n`);
398
+ }
399
+ await pressEnterToContinue();
400
+ }
401
+ }
402
+ catch (error) {
403
+ console.error("\n Error managing steps:", error);
404
+ await pressEnterToContinue();
405
+ }
406
+ }
407
+ async function generateSteps(config) {
408
+ try {
409
+ const wf = await selectWorkflow(config, "Select workflow:");
410
+ if (!wf)
411
+ return;
412
+ console.log("\n Enter the SOP (Standard Operating Procedure) document.");
413
+ console.log(" This will be used to generate workflow steps with AI.\n");
414
+ const sop = await input({
415
+ message: "SOP document text:",
416
+ validate: (v) => (v.length > 10 ? true : "Please provide more detail"),
417
+ });
418
+ console.log("\n Generating steps with AI... This may take a moment.\n");
419
+ const res = await fetch(`${config.apiUrl}/api/workflows/${wf.id}/generate-steps`, {
420
+ method: "POST",
421
+ headers: {
422
+ "Content-Type": "application/json",
423
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
424
+ },
425
+ body: JSON.stringify({ sop }),
426
+ });
427
+ if (!res.ok) {
428
+ const error = await res.json().catch(() => ({}));
429
+ console.error(`\n Error: ${error.error || `API returned ${res.status}`}\n`);
430
+ await pressEnterToContinue();
431
+ return;
432
+ }
433
+ const result = await res.json();
434
+ console.log(` ✓ Steps generated!`);
435
+ console.log(` Created ${result.stepsCreated || "?"} steps.\n`);
436
+ await pressEnterToContinue();
437
+ }
438
+ catch (error) {
439
+ console.error("\n Error generating steps:", error);
440
+ await pressEnterToContinue();
441
+ }
442
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Interactive Mode: Worktrees Module
3
+ *
4
+ * Provides menu-based worktree management for isolated agent workspaces.
5
+ */
6
+ export declare function worktreesMenu(): Promise<void>;