@simonfestl/husky-cli 0.5.0 → 0.5.2

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 (31) hide show
  1. package/README.md +21 -25
  2. package/dist/commands/config.d.ts +1 -0
  3. package/dist/commands/config.js +10 -3
  4. package/dist/commands/idea.js +9 -7
  5. package/dist/commands/interactive/changelog.d.ts +1 -0
  6. package/dist/commands/interactive/changelog.js +398 -0
  7. package/dist/commands/interactive/departments.d.ts +1 -0
  8. package/dist/commands/interactive/departments.js +242 -0
  9. package/dist/commands/interactive/ideas.d.ts +1 -0
  10. package/dist/commands/interactive/ideas.js +311 -0
  11. package/dist/commands/interactive/jules-sessions.d.ts +1 -0
  12. package/dist/commands/interactive/jules-sessions.js +460 -0
  13. package/dist/commands/interactive/processes.d.ts +1 -0
  14. package/dist/commands/interactive/processes.js +271 -0
  15. package/dist/commands/interactive/projects.d.ts +1 -0
  16. package/dist/commands/interactive/projects.js +297 -0
  17. package/dist/commands/interactive/roadmaps.d.ts +1 -0
  18. package/dist/commands/interactive/roadmaps.js +650 -0
  19. package/dist/commands/interactive/strategy.d.ts +1 -0
  20. package/dist/commands/interactive/strategy.js +790 -0
  21. package/dist/commands/interactive/tasks.d.ts +1 -0
  22. package/dist/commands/interactive/tasks.js +415 -0
  23. package/dist/commands/interactive/utils.d.ts +15 -0
  24. package/dist/commands/interactive/utils.js +54 -0
  25. package/dist/commands/interactive/vm-sessions.d.ts +1 -0
  26. package/dist/commands/interactive/vm-sessions.js +319 -0
  27. package/dist/commands/interactive/workflows.d.ts +1 -0
  28. package/dist/commands/interactive/workflows.js +442 -0
  29. package/dist/commands/interactive.js +150 -1135
  30. package/dist/index.js +1 -1
  31. package/package.json +1 -1
@@ -0,0 +1,242 @@
1
+ import { select, input, confirm } from "@inquirer/prompts";
2
+ import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
3
+ export async function departmentsMenu() {
4
+ const config = ensureConfig();
5
+ const menuItems = [
6
+ { name: "List all departments", value: "list" },
7
+ { name: "View department details", value: "view" },
8
+ { name: "Create new department", value: "create" },
9
+ { name: "Update department", value: "update" },
10
+ { name: "Delete department", value: "delete" },
11
+ { name: "Back to main menu", value: "back" },
12
+ ];
13
+ const choice = await select({
14
+ message: "Departments:",
15
+ choices: menuItems,
16
+ });
17
+ switch (choice) {
18
+ case "list":
19
+ await listDepartments(config);
20
+ break;
21
+ case "view":
22
+ await viewDepartment(config);
23
+ break;
24
+ case "create":
25
+ await createDepartment(config);
26
+ break;
27
+ case "update":
28
+ await updateDepartment(config);
29
+ break;
30
+ case "delete":
31
+ await deleteDepartment(config);
32
+ break;
33
+ case "back":
34
+ return;
35
+ }
36
+ }
37
+ async function fetchDepartments(config) {
38
+ const res = await fetch(`${config.apiUrl}/api/departments`, {
39
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
40
+ });
41
+ if (!res.ok)
42
+ throw new Error(`API returned ${res.status}`);
43
+ return res.json();
44
+ }
45
+ async function selectDepartment(config, message) {
46
+ const departments = await fetchDepartments(config);
47
+ if (departments.length === 0) {
48
+ console.log("\n No departments found.\n");
49
+ await pressEnterToContinue();
50
+ return null;
51
+ }
52
+ const choices = departments.map((d) => ({
53
+ name: `${d.icon || "📁"} ${truncate(d.name, 40)}`,
54
+ value: d.id,
55
+ }));
56
+ choices.push({ name: "Cancel", value: "__cancel__" });
57
+ const deptId = await select({ message, choices });
58
+ if (deptId === "__cancel__")
59
+ return null;
60
+ return departments.find((d) => d.id === deptId) || null;
61
+ }
62
+ async function listDepartments(config) {
63
+ try {
64
+ const departments = await fetchDepartments(config);
65
+ console.log("\n DEPARTMENTS");
66
+ console.log(" " + "-".repeat(70));
67
+ if (departments.length === 0) {
68
+ console.log(" No departments found.");
69
+ }
70
+ else {
71
+ for (const dept of departments) {
72
+ console.log(` ${dept.icon || "📁"} ${truncate(dept.name, 50)}`);
73
+ console.log(` ID: ${dept.id}`);
74
+ }
75
+ }
76
+ console.log("");
77
+ await pressEnterToContinue();
78
+ }
79
+ catch (error) {
80
+ console.error("\n Error fetching departments:", error);
81
+ await pressEnterToContinue();
82
+ }
83
+ }
84
+ async function viewDepartment(config) {
85
+ try {
86
+ const dept = await selectDepartment(config, "Select department to view:");
87
+ if (!dept)
88
+ return;
89
+ const res = await fetch(`${config.apiUrl}/api/departments/${dept.id}`, {
90
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
91
+ });
92
+ if (!res.ok) {
93
+ console.error(`\n Error: API returned ${res.status}\n`);
94
+ await pressEnterToContinue();
95
+ return;
96
+ }
97
+ const fullDept = await res.json();
98
+ console.log(`\n Department: ${fullDept.name}`);
99
+ console.log(" " + "-".repeat(50));
100
+ console.log(` ID: ${fullDept.id}`);
101
+ console.log(` Icon: ${fullDept.icon || "(none)"}`);
102
+ console.log(` Color: ${fullDept.color || "(none)"}`);
103
+ if (fullDept.description) {
104
+ console.log(` Description: ${fullDept.description}`);
105
+ }
106
+ console.log("");
107
+ await pressEnterToContinue();
108
+ }
109
+ catch (error) {
110
+ console.error("\n Error viewing department:", error);
111
+ await pressEnterToContinue();
112
+ }
113
+ }
114
+ async function createDepartment(config) {
115
+ try {
116
+ const name = await input({
117
+ message: "Department name:",
118
+ validate: (value) => (value.length > 0 ? true : "Name is required"),
119
+ });
120
+ const description = await input({
121
+ message: "Description (optional):",
122
+ });
123
+ const icon = await input({
124
+ message: "Icon emoji (optional, e.g. 🏢):",
125
+ default: "🏢",
126
+ });
127
+ const res = await fetch(`${config.apiUrl}/api/departments`, {
128
+ method: "POST",
129
+ headers: {
130
+ "Content-Type": "application/json",
131
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
132
+ },
133
+ body: JSON.stringify({
134
+ name,
135
+ description: description || undefined,
136
+ icon: icon || undefined,
137
+ }),
138
+ });
139
+ if (!res.ok) {
140
+ console.error(`\n Error: API returned ${res.status}\n`);
141
+ await pressEnterToContinue();
142
+ return;
143
+ }
144
+ const dept = await res.json();
145
+ console.log(`\n ✓ Department created!`);
146
+ console.log(` ID: ${dept.id}`);
147
+ console.log(` Name: ${dept.name}\n`);
148
+ await pressEnterToContinue();
149
+ }
150
+ catch (error) {
151
+ console.error("\n Error creating department:", error);
152
+ await pressEnterToContinue();
153
+ }
154
+ }
155
+ async function updateDepartment(config) {
156
+ try {
157
+ const dept = await selectDepartment(config, "Select department to update:");
158
+ if (!dept)
159
+ return;
160
+ const updateChoices = [
161
+ { name: "Name", value: "name" },
162
+ { name: "Description", value: "description" },
163
+ { name: "Icon", value: "icon" },
164
+ { name: "Cancel", value: "cancel" },
165
+ ];
166
+ const field = await select({ message: "What to update?", choices: updateChoices });
167
+ if (field === "cancel")
168
+ return;
169
+ let updateData = {};
170
+ switch (field) {
171
+ case "name":
172
+ updateData.name = await input({
173
+ message: "New name:",
174
+ default: dept.name,
175
+ validate: (v) => (v.length > 0 ? true : "Name required"),
176
+ });
177
+ break;
178
+ case "description":
179
+ updateData.description = await input({
180
+ message: "New description:",
181
+ default: dept.description || "",
182
+ });
183
+ break;
184
+ case "icon":
185
+ updateData.icon = await input({
186
+ message: "New icon emoji:",
187
+ default: dept.icon || "🏢",
188
+ });
189
+ break;
190
+ }
191
+ const res = await fetch(`${config.apiUrl}/api/departments/${dept.id}`, {
192
+ method: "PATCH",
193
+ headers: {
194
+ "Content-Type": "application/json",
195
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
196
+ },
197
+ body: JSON.stringify(updateData),
198
+ });
199
+ if (!res.ok) {
200
+ console.error(`\n Error: API returned ${res.status}\n`);
201
+ await pressEnterToContinue();
202
+ return;
203
+ }
204
+ console.log(`\n ✓ Department updated!\n`);
205
+ await pressEnterToContinue();
206
+ }
207
+ catch (error) {
208
+ console.error("\n Error updating department:", error);
209
+ await pressEnterToContinue();
210
+ }
211
+ }
212
+ async function deleteDepartment(config) {
213
+ try {
214
+ const dept = await selectDepartment(config, "Select department to delete:");
215
+ if (!dept)
216
+ return;
217
+ const confirmed = await confirm({
218
+ message: `Delete "${dept.name}"? This cannot be undone.`,
219
+ default: false,
220
+ });
221
+ if (!confirmed) {
222
+ console.log("\n Cancelled.\n");
223
+ await pressEnterToContinue();
224
+ return;
225
+ }
226
+ const res = await fetch(`${config.apiUrl}/api/departments/${dept.id}`, {
227
+ method: "DELETE",
228
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
229
+ });
230
+ if (!res.ok) {
231
+ console.error(`\n Error: API returned ${res.status}\n`);
232
+ await pressEnterToContinue();
233
+ return;
234
+ }
235
+ console.log(`\n ✓ Department deleted.\n`);
236
+ await pressEnterToContinue();
237
+ }
238
+ catch (error) {
239
+ console.error("\n Error deleting department:", error);
240
+ await pressEnterToContinue();
241
+ }
242
+ }
@@ -0,0 +1 @@
1
+ export declare function ideasMenu(): Promise<void>;
@@ -0,0 +1,311 @@
1
+ import { select, input, confirm } from "@inquirer/prompts";
2
+ import { ensureConfig, pressEnterToContinue, truncate, formatDate } from "./utils.js";
3
+ export async function ideasMenu() {
4
+ const config = ensureConfig();
5
+ const menuItems = [
6
+ { name: "List all ideas", value: "list" },
7
+ { name: "View idea details", value: "view" },
8
+ { name: "Create new idea", value: "create" },
9
+ { name: "Update idea", value: "update" },
10
+ { name: "Convert to task", value: "convert" },
11
+ { name: "Delete idea", value: "delete" },
12
+ { name: "Back to main menu", value: "back" },
13
+ ];
14
+ const choice = await select({
15
+ message: "Ideas:",
16
+ choices: menuItems,
17
+ });
18
+ switch (choice) {
19
+ case "list":
20
+ await listIdeas(config);
21
+ break;
22
+ case "view":
23
+ await viewIdea(config);
24
+ break;
25
+ case "create":
26
+ await createIdea(config);
27
+ break;
28
+ case "update":
29
+ await updateIdea(config);
30
+ break;
31
+ case "convert":
32
+ await convertIdea(config);
33
+ break;
34
+ case "delete":
35
+ await deleteIdea(config);
36
+ break;
37
+ case "back":
38
+ return;
39
+ }
40
+ }
41
+ async function fetchIdeas(config) {
42
+ const res = await fetch(`${config.apiUrl}/api/ideas`, {
43
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
44
+ });
45
+ if (!res.ok)
46
+ throw new Error(`API returned ${res.status}`);
47
+ return res.json();
48
+ }
49
+ async function selectIdea(config, message) {
50
+ const ideas = await fetchIdeas(config);
51
+ if (ideas.length === 0) {
52
+ console.log("\n No ideas found.\n");
53
+ await pressEnterToContinue();
54
+ return null;
55
+ }
56
+ const choices = ideas.map((i) => ({
57
+ name: `[${i.status || "draft"}] ${truncate(i.title || "(untitled)", 40)}`,
58
+ value: i.id,
59
+ }));
60
+ choices.push({ name: "Cancel", value: "__cancel__" });
61
+ const ideaId = await select({ message, choices });
62
+ if (ideaId === "__cancel__")
63
+ return null;
64
+ return ideas.find((i) => i.id === ideaId) || null;
65
+ }
66
+ async function listIdeas(config) {
67
+ try {
68
+ const ideas = await fetchIdeas(config);
69
+ console.log("\n IDEAS");
70
+ console.log(" " + "-".repeat(70));
71
+ if (ideas.length === 0) {
72
+ console.log(" No ideas found.");
73
+ }
74
+ else {
75
+ for (const idea of ideas) {
76
+ const statusIcon = idea.status === "active" ? "[o]" : idea.status === "converted" ? "[>]" : "[.]";
77
+ const title = idea.title || "(untitled)";
78
+ console.log(` ${statusIcon} ${truncate(title, 50).padEnd(50)} [${idea.status || "draft"}]`);
79
+ console.log(` ID: ${idea.id}`);
80
+ }
81
+ }
82
+ console.log("");
83
+ await pressEnterToContinue();
84
+ }
85
+ catch (error) {
86
+ console.error("\n Error fetching ideas:", error);
87
+ await pressEnterToContinue();
88
+ }
89
+ }
90
+ async function viewIdea(config) {
91
+ try {
92
+ const idea = await selectIdea(config, "Select idea to view:");
93
+ if (!idea)
94
+ return;
95
+ const res = await fetch(`${config.apiUrl}/api/ideas/${idea.id}`, {
96
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
97
+ });
98
+ if (!res.ok) {
99
+ console.error(`\n Error: API returned ${res.status}\n`);
100
+ await pressEnterToContinue();
101
+ return;
102
+ }
103
+ const fullIdea = await res.json();
104
+ console.log(`\n Idea: ${fullIdea.title || "(untitled)"}`);
105
+ console.log(" " + "-".repeat(50));
106
+ console.log(` ID: ${fullIdea.id}`);
107
+ console.log(` Status: ${fullIdea.status || "draft"}`);
108
+ if (fullIdea.category) {
109
+ console.log(` Category: ${fullIdea.category}`);
110
+ }
111
+ if (fullIdea.description) {
112
+ console.log(` Description:`);
113
+ console.log(` ${fullIdea.description}`);
114
+ }
115
+ console.log(` Created: ${formatDate(fullIdea.createdAt)}`);
116
+ console.log("");
117
+ await pressEnterToContinue();
118
+ }
119
+ catch (error) {
120
+ console.error("\n Error viewing idea:", error);
121
+ await pressEnterToContinue();
122
+ }
123
+ }
124
+ async function createIdea(config) {
125
+ try {
126
+ const title = await input({
127
+ message: "Idea title:",
128
+ validate: (value) => (value.length > 0 ? true : "Title is required"),
129
+ });
130
+ const description = await input({
131
+ message: "Description (optional):",
132
+ });
133
+ const category = await input({
134
+ message: "Category (optional):",
135
+ });
136
+ const res = await fetch(`${config.apiUrl}/api/ideas`, {
137
+ method: "POST",
138
+ headers: {
139
+ "Content-Type": "application/json",
140
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
141
+ },
142
+ body: JSON.stringify({
143
+ title,
144
+ description: description || undefined,
145
+ category: category || undefined,
146
+ status: "draft",
147
+ }),
148
+ });
149
+ if (!res.ok) {
150
+ console.error(`\n Error: API returned ${res.status}\n`);
151
+ await pressEnterToContinue();
152
+ return;
153
+ }
154
+ const idea = await res.json();
155
+ console.log(`\n ✓ Idea created!`);
156
+ console.log(` ID: ${idea.id}`);
157
+ console.log(` Title: ${idea.title}\n`);
158
+ await pressEnterToContinue();
159
+ }
160
+ catch (error) {
161
+ console.error("\n Error creating idea:", error);
162
+ await pressEnterToContinue();
163
+ }
164
+ }
165
+ async function updateIdea(config) {
166
+ try {
167
+ const idea = await selectIdea(config, "Select idea to update:");
168
+ if (!idea)
169
+ return;
170
+ const updateChoices = [
171
+ { name: "Title", value: "title" },
172
+ { name: "Description", value: "description" },
173
+ { name: "Status", value: "status" },
174
+ { name: "Category", value: "category" },
175
+ { name: "Cancel", value: "cancel" },
176
+ ];
177
+ const field = await select({ message: "What to update?", choices: updateChoices });
178
+ if (field === "cancel")
179
+ return;
180
+ let updateData = {};
181
+ switch (field) {
182
+ case "title":
183
+ updateData.title = await input({
184
+ message: "New title:",
185
+ default: idea.title,
186
+ validate: (v) => (v.length > 0 ? true : "Title required"),
187
+ });
188
+ break;
189
+ case "description":
190
+ updateData.description = await input({
191
+ message: "New description:",
192
+ default: idea.description || "",
193
+ });
194
+ break;
195
+ case "status":
196
+ updateData.status = await select({
197
+ message: "New status:",
198
+ choices: [
199
+ { name: "Draft", value: "draft" },
200
+ { name: "Active", value: "active" },
201
+ { name: "Archived", value: "archived" },
202
+ ],
203
+ default: idea.status || "draft",
204
+ });
205
+ break;
206
+ case "category":
207
+ updateData.category = await input({
208
+ message: "New category:",
209
+ default: idea.category || "",
210
+ });
211
+ break;
212
+ }
213
+ const res = await fetch(`${config.apiUrl}/api/ideas/${idea.id}`, {
214
+ method: "PATCH",
215
+ headers: {
216
+ "Content-Type": "application/json",
217
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
218
+ },
219
+ body: JSON.stringify(updateData),
220
+ });
221
+ if (!res.ok) {
222
+ console.error(`\n Error: API returned ${res.status}\n`);
223
+ await pressEnterToContinue();
224
+ return;
225
+ }
226
+ console.log(`\n ✓ Idea updated!\n`);
227
+ await pressEnterToContinue();
228
+ }
229
+ catch (error) {
230
+ console.error("\n Error updating idea:", error);
231
+ await pressEnterToContinue();
232
+ }
233
+ }
234
+ async function convertIdea(config) {
235
+ try {
236
+ const idea = await selectIdea(config, "Select idea to convert to task:");
237
+ if (!idea)
238
+ return;
239
+ const priority = await select({
240
+ message: "Task priority:",
241
+ choices: [
242
+ { name: "Low", value: "low" },
243
+ { name: "Medium", value: "medium" },
244
+ { name: "High", value: "high" },
245
+ ],
246
+ default: "medium",
247
+ });
248
+ const assignee = await select({
249
+ message: "Assign to:",
250
+ choices: [
251
+ { name: "Human", value: "human" },
252
+ { name: "LLM Agent", value: "llm" },
253
+ { name: "Unassigned", value: "unassigned" },
254
+ ],
255
+ default: "unassigned",
256
+ });
257
+ const res = await fetch(`${config.apiUrl}/api/ideas/${idea.id}/convert`, {
258
+ method: "POST",
259
+ headers: {
260
+ "Content-Type": "application/json",
261
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
262
+ },
263
+ body: JSON.stringify({ priority, assignee }),
264
+ });
265
+ if (!res.ok) {
266
+ console.error(`\n Error: API returned ${res.status}\n`);
267
+ await pressEnterToContinue();
268
+ return;
269
+ }
270
+ const result = await res.json();
271
+ console.log(`\n ✓ Idea converted to task!`);
272
+ console.log(` Task ID: ${result.taskId}`);
273
+ console.log(` Title: ${result.title}\n`);
274
+ await pressEnterToContinue();
275
+ }
276
+ catch (error) {
277
+ console.error("\n Error converting idea:", error);
278
+ await pressEnterToContinue();
279
+ }
280
+ }
281
+ async function deleteIdea(config) {
282
+ try {
283
+ const idea = await selectIdea(config, "Select idea to delete:");
284
+ if (!idea)
285
+ return;
286
+ const confirmed = await confirm({
287
+ message: `Delete "${idea.title}"? 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/ideas/${idea.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 ✓ Idea deleted.\n`);
305
+ await pressEnterToContinue();
306
+ }
307
+ catch (error) {
308
+ console.error("\n Error deleting idea:", error);
309
+ await pressEnterToContinue();
310
+ }
311
+ }
@@ -0,0 +1 @@
1
+ export declare function julesSessionsMenu(): Promise<void>;