@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.
- package/README.md +21 -25
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +10 -3
- package/dist/commands/idea.js +9 -7
- package/dist/commands/interactive/changelog.d.ts +1 -0
- package/dist/commands/interactive/changelog.js +398 -0
- package/dist/commands/interactive/departments.d.ts +1 -0
- package/dist/commands/interactive/departments.js +242 -0
- package/dist/commands/interactive/ideas.d.ts +1 -0
- package/dist/commands/interactive/ideas.js +311 -0
- package/dist/commands/interactive/jules-sessions.d.ts +1 -0
- package/dist/commands/interactive/jules-sessions.js +460 -0
- package/dist/commands/interactive/processes.d.ts +1 -0
- package/dist/commands/interactive/processes.js +271 -0
- package/dist/commands/interactive/projects.d.ts +1 -0
- package/dist/commands/interactive/projects.js +297 -0
- package/dist/commands/interactive/roadmaps.d.ts +1 -0
- package/dist/commands/interactive/roadmaps.js +650 -0
- package/dist/commands/interactive/strategy.d.ts +1 -0
- package/dist/commands/interactive/strategy.js +790 -0
- package/dist/commands/interactive/tasks.d.ts +1 -0
- package/dist/commands/interactive/tasks.js +415 -0
- package/dist/commands/interactive/utils.d.ts +15 -0
- package/dist/commands/interactive/utils.js +54 -0
- package/dist/commands/interactive/vm-sessions.d.ts +1 -0
- package/dist/commands/interactive/vm-sessions.js +319 -0
- package/dist/commands/interactive/workflows.d.ts +1 -0
- package/dist/commands/interactive/workflows.js +442 -0
- package/dist/commands/interactive.js +150 -1135
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { select, input, confirm } from "@inquirer/prompts";
|
|
2
|
+
import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
|
|
3
|
+
export async function processesMenu() {
|
|
4
|
+
const config = ensureConfig();
|
|
5
|
+
const menuItems = [
|
|
6
|
+
{ name: "List all processes", value: "list" },
|
|
7
|
+
{ name: "View process details", value: "view" },
|
|
8
|
+
{ name: "Create new process", value: "create" },
|
|
9
|
+
{ name: "Update process", value: "update" },
|
|
10
|
+
{ name: "Delete process", value: "delete" },
|
|
11
|
+
{ name: "Back to main menu", value: "back" },
|
|
12
|
+
];
|
|
13
|
+
const choice = await select({
|
|
14
|
+
message: "Processes:",
|
|
15
|
+
choices: menuItems,
|
|
16
|
+
});
|
|
17
|
+
switch (choice) {
|
|
18
|
+
case "list":
|
|
19
|
+
await listProcesses(config);
|
|
20
|
+
break;
|
|
21
|
+
case "view":
|
|
22
|
+
await viewProcess(config);
|
|
23
|
+
break;
|
|
24
|
+
case "create":
|
|
25
|
+
await createProcess(config);
|
|
26
|
+
break;
|
|
27
|
+
case "update":
|
|
28
|
+
await updateProcess(config);
|
|
29
|
+
break;
|
|
30
|
+
case "delete":
|
|
31
|
+
await deleteProcess(config);
|
|
32
|
+
break;
|
|
33
|
+
case "back":
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function fetchProcesses(config) {
|
|
38
|
+
const res = await fetch(`${config.apiUrl}/api/processes`, {
|
|
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 selectProcess(config, message) {
|
|
46
|
+
const processes = await fetchProcesses(config);
|
|
47
|
+
if (processes.length === 0) {
|
|
48
|
+
console.log("\n No processes found.\n");
|
|
49
|
+
await pressEnterToContinue();
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const choices = processes.map((p) => ({
|
|
53
|
+
name: `[${p.status}] ${truncate(p.name, 40)}`,
|
|
54
|
+
value: p.id,
|
|
55
|
+
}));
|
|
56
|
+
choices.push({ name: "Cancel", value: "__cancel__" });
|
|
57
|
+
const processId = await select({ message, choices });
|
|
58
|
+
if (processId === "__cancel__")
|
|
59
|
+
return null;
|
|
60
|
+
return processes.find((p) => p.id === processId) || null;
|
|
61
|
+
}
|
|
62
|
+
async function listProcesses(config) {
|
|
63
|
+
try {
|
|
64
|
+
const processes = await fetchProcesses(config);
|
|
65
|
+
console.log("\n PROCESSES");
|
|
66
|
+
console.log(" " + "-".repeat(70));
|
|
67
|
+
if (processes.length === 0) {
|
|
68
|
+
console.log(" No processes found.");
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
for (const proc of processes) {
|
|
72
|
+
console.log(` [${proc.status}] ${truncate(proc.name, 50)}`);
|
|
73
|
+
console.log(` ID: ${proc.id}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log("");
|
|
77
|
+
await pressEnterToContinue();
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error("\n Error fetching processes:", error);
|
|
81
|
+
await pressEnterToContinue();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function viewProcess(config) {
|
|
85
|
+
try {
|
|
86
|
+
const proc = await selectProcess(config, "Select process to view:");
|
|
87
|
+
if (!proc)
|
|
88
|
+
return;
|
|
89
|
+
const res = await fetch(`${config.apiUrl}/api/processes/${proc.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 fullProc = await res.json();
|
|
98
|
+
console.log(`\n Process: ${fullProc.name}`);
|
|
99
|
+
console.log(" " + "-".repeat(50));
|
|
100
|
+
console.log(` ID: ${fullProc.id}`);
|
|
101
|
+
console.log(` Status: ${fullProc.status}`);
|
|
102
|
+
if (fullProc.valueStream) {
|
|
103
|
+
console.log(` Value Stream: ${fullProc.valueStream.replace(/_/g, " ")}`);
|
|
104
|
+
}
|
|
105
|
+
if (fullProc.description) {
|
|
106
|
+
console.log(` Description: ${fullProc.description}`);
|
|
107
|
+
}
|
|
108
|
+
console.log("");
|
|
109
|
+
await pressEnterToContinue();
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error("\n Error viewing process:", error);
|
|
113
|
+
await pressEnterToContinue();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function createProcess(config) {
|
|
117
|
+
try {
|
|
118
|
+
const name = await input({
|
|
119
|
+
message: "Process name:",
|
|
120
|
+
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
121
|
+
});
|
|
122
|
+
const description = await input({
|
|
123
|
+
message: "Description (optional):",
|
|
124
|
+
});
|
|
125
|
+
const valueStream = await select({
|
|
126
|
+
message: "Value stream:",
|
|
127
|
+
choices: [
|
|
128
|
+
{ name: "Order to Delivery", value: "order_to_delivery" },
|
|
129
|
+
{ name: "Procure to Pay", value: "procure_to_pay" },
|
|
130
|
+
{ name: "Returns Management", value: "returns_management" },
|
|
131
|
+
{ name: "Product Lifecycle", value: "product_lifecycle" },
|
|
132
|
+
{ name: "Customer Service", value: "customer_service" },
|
|
133
|
+
{ name: "Finance Admin", value: "finance_admin" },
|
|
134
|
+
],
|
|
135
|
+
default: "customer_service",
|
|
136
|
+
});
|
|
137
|
+
const status = await select({
|
|
138
|
+
message: "Status:",
|
|
139
|
+
choices: [
|
|
140
|
+
{ name: "Owner Only", value: "owner_only" },
|
|
141
|
+
{ name: "Approval Needed", value: "approval_needed" },
|
|
142
|
+
{ name: "Supervised", value: "supervised" },
|
|
143
|
+
{ name: "Delegated", value: "delegated" },
|
|
144
|
+
{ name: "Automated", value: "automated" },
|
|
145
|
+
],
|
|
146
|
+
default: "owner_only",
|
|
147
|
+
});
|
|
148
|
+
const res = await fetch(`${config.apiUrl}/api/processes`, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
name,
|
|
156
|
+
description: description || undefined,
|
|
157
|
+
valueStream,
|
|
158
|
+
status,
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok) {
|
|
162
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
163
|
+
await pressEnterToContinue();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const proc = await res.json();
|
|
167
|
+
console.log(`\n ✓ Process created!`);
|
|
168
|
+
console.log(` ID: ${proc.id}`);
|
|
169
|
+
console.log(` Name: ${proc.name}\n`);
|
|
170
|
+
await pressEnterToContinue();
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error("\n Error creating process:", error);
|
|
174
|
+
await pressEnterToContinue();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function updateProcess(config) {
|
|
178
|
+
try {
|
|
179
|
+
const proc = await selectProcess(config, "Select process to update:");
|
|
180
|
+
if (!proc)
|
|
181
|
+
return;
|
|
182
|
+
const updateChoices = [
|
|
183
|
+
{ name: "Name", value: "name" },
|
|
184
|
+
{ name: "Description", value: "description" },
|
|
185
|
+
{ name: "Status", value: "status" },
|
|
186
|
+
{ name: "Cancel", value: "cancel" },
|
|
187
|
+
];
|
|
188
|
+
const field = await select({ message: "What to update?", choices: updateChoices });
|
|
189
|
+
if (field === "cancel")
|
|
190
|
+
return;
|
|
191
|
+
let updateData = {};
|
|
192
|
+
switch (field) {
|
|
193
|
+
case "name":
|
|
194
|
+
updateData.name = await input({
|
|
195
|
+
message: "New name:",
|
|
196
|
+
default: proc.name,
|
|
197
|
+
validate: (v) => (v.length > 0 ? true : "Name required"),
|
|
198
|
+
});
|
|
199
|
+
break;
|
|
200
|
+
case "description":
|
|
201
|
+
updateData.description = await input({
|
|
202
|
+
message: "New description:",
|
|
203
|
+
default: proc.description || "",
|
|
204
|
+
});
|
|
205
|
+
break;
|
|
206
|
+
case "status":
|
|
207
|
+
updateData.status = await select({
|
|
208
|
+
message: "New status:",
|
|
209
|
+
choices: [
|
|
210
|
+
{ name: "Owner Only", value: "owner_only" },
|
|
211
|
+
{ name: "Approval Needed", value: "approval_needed" },
|
|
212
|
+
{ name: "Supervised", value: "supervised" },
|
|
213
|
+
{ name: "Delegated", value: "delegated" },
|
|
214
|
+
{ name: "Automated", value: "automated" },
|
|
215
|
+
],
|
|
216
|
+
default: proc.status,
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
const res = await fetch(`${config.apiUrl}/api/processes/${proc.id}`, {
|
|
221
|
+
method: "PATCH",
|
|
222
|
+
headers: {
|
|
223
|
+
"Content-Type": "application/json",
|
|
224
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
225
|
+
},
|
|
226
|
+
body: JSON.stringify(updateData),
|
|
227
|
+
});
|
|
228
|
+
if (!res.ok) {
|
|
229
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
230
|
+
await pressEnterToContinue();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
console.log(`\n ✓ Process updated!\n`);
|
|
234
|
+
await pressEnterToContinue();
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console.error("\n Error updating process:", error);
|
|
238
|
+
await pressEnterToContinue();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function deleteProcess(config) {
|
|
242
|
+
try {
|
|
243
|
+
const proc = await selectProcess(config, "Select process to delete:");
|
|
244
|
+
if (!proc)
|
|
245
|
+
return;
|
|
246
|
+
const confirmed = await confirm({
|
|
247
|
+
message: `Delete "${proc.name}"? This cannot be undone.`,
|
|
248
|
+
default: false,
|
|
249
|
+
});
|
|
250
|
+
if (!confirmed) {
|
|
251
|
+
console.log("\n Cancelled.\n");
|
|
252
|
+
await pressEnterToContinue();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const res = await fetch(`${config.apiUrl}/api/processes/${proc.id}`, {
|
|
256
|
+
method: "DELETE",
|
|
257
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
258
|
+
});
|
|
259
|
+
if (!res.ok) {
|
|
260
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
261
|
+
await pressEnterToContinue();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
console.log(`\n ✓ Process deleted.\n`);
|
|
265
|
+
await pressEnterToContinue();
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
console.error("\n Error deleting process:", error);
|
|
269
|
+
await pressEnterToContinue();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function projectsMenu(): Promise<void>;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { select, input, confirm } from "@inquirer/prompts";
|
|
2
|
+
import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
|
|
3
|
+
export async function projectsMenu() {
|
|
4
|
+
const config = ensureConfig();
|
|
5
|
+
const menuItems = [
|
|
6
|
+
{ name: "List all projects", value: "list" },
|
|
7
|
+
{ name: "View project details", value: "view" },
|
|
8
|
+
{ name: "Create new project", value: "create" },
|
|
9
|
+
{ name: "Update project", value: "update" },
|
|
10
|
+
{ name: "Delete project", value: "delete" },
|
|
11
|
+
{ name: "Back to main menu", value: "back" },
|
|
12
|
+
];
|
|
13
|
+
const choice = await select({
|
|
14
|
+
message: "Projects:",
|
|
15
|
+
choices: menuItems,
|
|
16
|
+
});
|
|
17
|
+
switch (choice) {
|
|
18
|
+
case "list":
|
|
19
|
+
await listProjects(config);
|
|
20
|
+
break;
|
|
21
|
+
case "view":
|
|
22
|
+
await viewProject(config);
|
|
23
|
+
break;
|
|
24
|
+
case "create":
|
|
25
|
+
await createProject(config);
|
|
26
|
+
break;
|
|
27
|
+
case "update":
|
|
28
|
+
await updateProject(config);
|
|
29
|
+
break;
|
|
30
|
+
case "delete":
|
|
31
|
+
await deleteProject(config);
|
|
32
|
+
break;
|
|
33
|
+
case "back":
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function fetchProjects(config) {
|
|
38
|
+
const res = await fetch(`${config.apiUrl}/api/projects`, {
|
|
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 selectProject(config, message) {
|
|
46
|
+
const projects = await fetchProjects(config);
|
|
47
|
+
if (projects.length === 0) {
|
|
48
|
+
console.log("\n No projects found.\n");
|
|
49
|
+
await pressEnterToContinue();
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const choices = projects.map((p) => ({
|
|
53
|
+
name: `[${p.status}] ${truncate(p.name, 40)} (${p.workStatus})`,
|
|
54
|
+
value: p.id,
|
|
55
|
+
}));
|
|
56
|
+
choices.push({ name: "Cancel", value: "__cancel__" });
|
|
57
|
+
const projectId = await select({ message, choices });
|
|
58
|
+
if (projectId === "__cancel__")
|
|
59
|
+
return null;
|
|
60
|
+
return projects.find((p) => p.id === projectId) || null;
|
|
61
|
+
}
|
|
62
|
+
async function listProjects(config) {
|
|
63
|
+
try {
|
|
64
|
+
const projects = await fetchProjects(config);
|
|
65
|
+
console.log("\n PROJECTS");
|
|
66
|
+
console.log(" " + "-".repeat(70));
|
|
67
|
+
if (projects.length === 0) {
|
|
68
|
+
console.log(" No projects found.");
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
for (const project of projects) {
|
|
72
|
+
const statusIcon = project.status === "active" ? "[+]" : "[-]";
|
|
73
|
+
console.log(` ${statusIcon} ${truncate(project.name, 40).padEnd(40)} [${project.workStatus}]`);
|
|
74
|
+
console.log(` ID: ${project.id}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
console.log("");
|
|
78
|
+
await pressEnterToContinue();
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error("\n Error fetching projects:", error);
|
|
82
|
+
await pressEnterToContinue();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function viewProject(config) {
|
|
86
|
+
try {
|
|
87
|
+
const project = await selectProject(config, "Select project to view:");
|
|
88
|
+
if (!project)
|
|
89
|
+
return;
|
|
90
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${project.id}`, {
|
|
91
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
95
|
+
await pressEnterToContinue();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const fullProject = await res.json();
|
|
99
|
+
console.log(`\n Project: ${fullProject.name}`);
|
|
100
|
+
console.log(" " + "-".repeat(50));
|
|
101
|
+
console.log(` ID: ${fullProject.id}`);
|
|
102
|
+
console.log(` Status: ${fullProject.status}`);
|
|
103
|
+
console.log(` Work Status: ${fullProject.workStatus}`);
|
|
104
|
+
if (fullProject.description) {
|
|
105
|
+
console.log(` Description: ${fullProject.description}`);
|
|
106
|
+
}
|
|
107
|
+
if (fullProject.techStack) {
|
|
108
|
+
console.log(` Tech Stack: ${fullProject.techStack}`);
|
|
109
|
+
}
|
|
110
|
+
if (fullProject.githubRepo) {
|
|
111
|
+
console.log(` GitHub: ${fullProject.githubRepo}`);
|
|
112
|
+
}
|
|
113
|
+
console.log("");
|
|
114
|
+
await pressEnterToContinue();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error("\n Error viewing project:", error);
|
|
118
|
+
await pressEnterToContinue();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function createProject(config) {
|
|
122
|
+
try {
|
|
123
|
+
const name = await input({
|
|
124
|
+
message: "Project name:",
|
|
125
|
+
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
126
|
+
});
|
|
127
|
+
const description = await input({
|
|
128
|
+
message: "Description (optional):",
|
|
129
|
+
});
|
|
130
|
+
const workStatus = await select({
|
|
131
|
+
message: "Work status:",
|
|
132
|
+
choices: [
|
|
133
|
+
{ name: "Planning", value: "planning" },
|
|
134
|
+
{ name: "In Progress", value: "in_progress" },
|
|
135
|
+
{ name: "Review", value: "review" },
|
|
136
|
+
{ name: "Completed", value: "completed" },
|
|
137
|
+
{ name: "On Hold", value: "on_hold" },
|
|
138
|
+
],
|
|
139
|
+
default: "planning",
|
|
140
|
+
});
|
|
141
|
+
const techStack = await input({
|
|
142
|
+
message: "Tech stack (optional):",
|
|
143
|
+
});
|
|
144
|
+
const githubRepo = await input({
|
|
145
|
+
message: "GitHub repository URL (optional):",
|
|
146
|
+
});
|
|
147
|
+
const res = await fetch(`${config.apiUrl}/api/projects`, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: {
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
name,
|
|
155
|
+
description: description || undefined,
|
|
156
|
+
status: "active",
|
|
157
|
+
workStatus,
|
|
158
|
+
techStack: techStack || undefined,
|
|
159
|
+
githubRepo: githubRepo || undefined,
|
|
160
|
+
}),
|
|
161
|
+
});
|
|
162
|
+
if (!res.ok) {
|
|
163
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
164
|
+
await pressEnterToContinue();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const project = await res.json();
|
|
168
|
+
console.log(`\n ✓ Project created!`);
|
|
169
|
+
console.log(` ID: ${project.id}`);
|
|
170
|
+
console.log(` Name: ${project.name}\n`);
|
|
171
|
+
await pressEnterToContinue();
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
console.error("\n Error creating project:", error);
|
|
175
|
+
await pressEnterToContinue();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function updateProject(config) {
|
|
179
|
+
try {
|
|
180
|
+
const project = await selectProject(config, "Select project to update:");
|
|
181
|
+
if (!project)
|
|
182
|
+
return;
|
|
183
|
+
const updateChoices = [
|
|
184
|
+
{ name: "Name", value: "name" },
|
|
185
|
+
{ name: "Description", value: "description" },
|
|
186
|
+
{ name: "Status", value: "status" },
|
|
187
|
+
{ name: "Work Status", value: "workStatus" },
|
|
188
|
+
{ name: "Tech Stack", value: "techStack" },
|
|
189
|
+
{ name: "GitHub Repo", value: "githubRepo" },
|
|
190
|
+
{ name: "Cancel", value: "cancel" },
|
|
191
|
+
];
|
|
192
|
+
const field = await select({ message: "What to update?", choices: updateChoices });
|
|
193
|
+
if (field === "cancel")
|
|
194
|
+
return;
|
|
195
|
+
let updateData = {};
|
|
196
|
+
switch (field) {
|
|
197
|
+
case "name":
|
|
198
|
+
updateData.name = await input({
|
|
199
|
+
message: "New name:",
|
|
200
|
+
default: project.name,
|
|
201
|
+
validate: (v) => (v.length > 0 ? true : "Name required"),
|
|
202
|
+
});
|
|
203
|
+
break;
|
|
204
|
+
case "description":
|
|
205
|
+
updateData.description = await input({
|
|
206
|
+
message: "New description:",
|
|
207
|
+
default: project.description || "",
|
|
208
|
+
});
|
|
209
|
+
break;
|
|
210
|
+
case "status":
|
|
211
|
+
updateData.status = await select({
|
|
212
|
+
message: "New status:",
|
|
213
|
+
choices: [
|
|
214
|
+
{ name: "Active", value: "active" },
|
|
215
|
+
{ name: "Archived", value: "archived" },
|
|
216
|
+
],
|
|
217
|
+
default: project.status,
|
|
218
|
+
});
|
|
219
|
+
break;
|
|
220
|
+
case "workStatus":
|
|
221
|
+
updateData.workStatus = await select({
|
|
222
|
+
message: "New work status:",
|
|
223
|
+
choices: [
|
|
224
|
+
{ name: "Planning", value: "planning" },
|
|
225
|
+
{ name: "In Progress", value: "in_progress" },
|
|
226
|
+
{ name: "Review", value: "review" },
|
|
227
|
+
{ name: "Completed", value: "completed" },
|
|
228
|
+
{ name: "On Hold", value: "on_hold" },
|
|
229
|
+
],
|
|
230
|
+
default: project.workStatus,
|
|
231
|
+
});
|
|
232
|
+
break;
|
|
233
|
+
case "techStack":
|
|
234
|
+
updateData.techStack = await input({
|
|
235
|
+
message: "New tech stack:",
|
|
236
|
+
default: project.techStack || "",
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
case "githubRepo":
|
|
240
|
+
updateData.githubRepo = await input({
|
|
241
|
+
message: "New GitHub repo URL:",
|
|
242
|
+
default: project.githubRepo || "",
|
|
243
|
+
});
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${project.id}`, {
|
|
247
|
+
method: "PATCH",
|
|
248
|
+
headers: {
|
|
249
|
+
"Content-Type": "application/json",
|
|
250
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
251
|
+
},
|
|
252
|
+
body: JSON.stringify(updateData),
|
|
253
|
+
});
|
|
254
|
+
if (!res.ok) {
|
|
255
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
256
|
+
await pressEnterToContinue();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
console.log(`\n ✓ Project updated!\n`);
|
|
260
|
+
await pressEnterToContinue();
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
console.error("\n Error updating project:", error);
|
|
264
|
+
await pressEnterToContinue();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function deleteProject(config) {
|
|
268
|
+
try {
|
|
269
|
+
const project = await selectProject(config, "Select project to delete:");
|
|
270
|
+
if (!project)
|
|
271
|
+
return;
|
|
272
|
+
const confirmed = await confirm({
|
|
273
|
+
message: `Delete "${project.name}"? This cannot be undone.`,
|
|
274
|
+
default: false,
|
|
275
|
+
});
|
|
276
|
+
if (!confirmed) {
|
|
277
|
+
console.log("\n Cancelled.\n");
|
|
278
|
+
await pressEnterToContinue();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${project.id}`, {
|
|
282
|
+
method: "DELETE",
|
|
283
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
284
|
+
});
|
|
285
|
+
if (!res.ok) {
|
|
286
|
+
console.error(`\n Error: API returned ${res.status}\n`);
|
|
287
|
+
await pressEnterToContinue();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
console.log(`\n ✓ Project deleted.\n`);
|
|
291
|
+
await pressEnterToContinue();
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
console.error("\n Error deleting project:", error);
|
|
295
|
+
await pressEnterToContinue();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function roadmapsMenu(): Promise<void>;
|