@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.
- package/dist/commands/config.js +4 -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/worktrees.d.ts +6 -0
- package/dist/commands/interactive/worktrees.js +354 -0
- package/dist/commands/interactive.js +118 -1208
- package/dist/commands/worktree.d.ts +2 -0
- package/dist/commands/worktree.js +404 -0
- package/dist/index.js +3 -1
- package/dist/lib/merge-lock.d.ts +83 -0
- package/dist/lib/merge-lock.js +242 -0
- package/dist/lib/worktree.d.ts +133 -0
- package/dist/lib/worktree.js +473 -0
- package/package.json +1 -1
|
@@ -1,1204 +1,124 @@
|
|
|
1
|
-
import { select, input
|
|
2
|
-
import { getConfig } from "./config.js";
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function printHeader() {
|
|
18
|
-
const config = getConfig();
|
|
19
|
-
console.log("\n");
|
|
20
|
-
console.log(" 🐕 Husky CLI - Interactive Mode");
|
|
21
|
-
console.log(" " + "─".repeat(35));
|
|
22
|
-
if (!config.apiUrl) {
|
|
23
|
-
console.log("");
|
|
24
|
-
console.log(" ⚠️ Getting Started:");
|
|
25
|
-
console.log(" Go to 'CLI Config' to set up your API URL and Key");
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.log(` Connected to: ${config.apiUrl.substring(0, 40)}`);
|
|
29
|
-
}
|
|
30
|
-
console.log("");
|
|
31
|
-
}
|
|
32
|
-
// ============================================
|
|
33
|
-
// MAIN MENU
|
|
34
|
-
// ============================================
|
|
35
|
-
export async function runInteractiveMode() {
|
|
36
|
-
let running = true;
|
|
37
|
-
while (running) {
|
|
38
|
-
clearScreen();
|
|
39
|
-
printHeader();
|
|
40
|
-
const mainMenuItems = [
|
|
41
|
-
{ name: "Tasks", value: "tasks", description: "Manage tasks" },
|
|
42
|
-
{ name: "Projects", value: "projects", description: "Manage projects" },
|
|
43
|
-
{ name: "Roadmaps", value: "roadmaps", description: "Manage roadmaps" },
|
|
44
|
-
{ name: "Workflows", value: "workflows", description: "Manage workflows" },
|
|
45
|
-
{ name: "Ideas", value: "ideas", description: "Manage ideas" },
|
|
46
|
-
{ name: "VM Sessions", value: "vm", description: "Manage VM sessions" },
|
|
47
|
-
{ name: "Jules Sessions", value: "jules", description: "Manage Jules AI sessions" },
|
|
48
|
-
{ name: "Business Strategy", value: "strategy", description: "Manage business strategy" },
|
|
49
|
-
{ name: "Dashboard Settings", value: "settings", description: "Manage dashboard settings" },
|
|
50
|
-
{ name: "CLI Config", value: "config", description: "Configure CLI (API URL, API Key)" },
|
|
51
|
-
{ name: "---", value: "separator", description: "" },
|
|
52
|
-
{ name: "Exit", value: "exit", description: "Exit interactive mode" },
|
|
53
|
-
];
|
|
54
|
-
try {
|
|
55
|
-
const choice = await select({
|
|
56
|
-
message: "Select an option:",
|
|
57
|
-
choices: mainMenuItems.filter((item) => item.value !== "separator"),
|
|
58
|
-
});
|
|
59
|
-
switch (choice) {
|
|
60
|
-
case "tasks":
|
|
61
|
-
await tasksMenu();
|
|
62
|
-
break;
|
|
63
|
-
case "projects":
|
|
64
|
-
await projectsMenu();
|
|
65
|
-
break;
|
|
66
|
-
case "roadmaps":
|
|
67
|
-
await roadmapsMenu();
|
|
68
|
-
break;
|
|
69
|
-
case "workflows":
|
|
70
|
-
await workflowsMenu();
|
|
71
|
-
break;
|
|
72
|
-
case "ideas":
|
|
73
|
-
await ideasMenu();
|
|
74
|
-
break;
|
|
75
|
-
case "vm":
|
|
76
|
-
await vmSessionsMenu();
|
|
77
|
-
break;
|
|
78
|
-
case "jules":
|
|
79
|
-
await julesSessionsMenu();
|
|
80
|
-
break;
|
|
81
|
-
case "strategy":
|
|
82
|
-
await strategyMenu();
|
|
83
|
-
break;
|
|
84
|
-
case "settings":
|
|
85
|
-
await settingsMenu();
|
|
86
|
-
break;
|
|
87
|
-
case "config":
|
|
88
|
-
await cliConfigMenu();
|
|
89
|
-
break;
|
|
90
|
-
case "exit":
|
|
91
|
-
running = false;
|
|
92
|
-
console.log("\n Goodbye!\n");
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
// User pressed Ctrl+C or escaped
|
|
98
|
-
if (error.name === "ExitPromptError") {
|
|
99
|
-
running = false;
|
|
100
|
-
console.log("\n Goodbye!\n");
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// ============================================
|
|
109
|
-
// TASKS MENU
|
|
110
|
-
// ============================================
|
|
111
|
-
async function tasksMenu() {
|
|
112
|
-
const config = ensureConfig();
|
|
113
|
-
const menuItems = [
|
|
114
|
-
{ name: "List all tasks", value: "list" },
|
|
115
|
-
{ name: "Create new task", value: "create" },
|
|
116
|
-
{ name: "Search tasks by status", value: "search" },
|
|
117
|
-
{ name: "Back to main menu", value: "back" },
|
|
118
|
-
];
|
|
119
|
-
const choice = await select({
|
|
120
|
-
message: "Tasks:",
|
|
121
|
-
choices: menuItems,
|
|
122
|
-
});
|
|
123
|
-
switch (choice) {
|
|
124
|
-
case "list":
|
|
125
|
-
await listTasks(config);
|
|
126
|
-
break;
|
|
127
|
-
case "create":
|
|
128
|
-
await createTask(config);
|
|
129
|
-
break;
|
|
130
|
-
case "search":
|
|
131
|
-
await searchTasks(config);
|
|
132
|
-
break;
|
|
133
|
-
case "back":
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async function listTasks(config) {
|
|
138
|
-
try {
|
|
139
|
-
const res = await fetch(`${config.apiUrl}/api/tasks`, {
|
|
140
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
141
|
-
});
|
|
142
|
-
if (!res.ok) {
|
|
143
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
144
|
-
await pressEnterToContinue();
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const tasks = await res.json();
|
|
148
|
-
console.log("\n TASKS");
|
|
149
|
-
console.log(" " + "-".repeat(70));
|
|
150
|
-
if (tasks.length === 0) {
|
|
151
|
-
console.log(" No tasks found.");
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
for (const task of tasks) {
|
|
155
|
-
const statusIcon = task.status === "done" ? "[x]" : task.status === "in_progress" ? "[>]" : "[ ]";
|
|
156
|
-
console.log(` ${statusIcon} ${task.title.substring(0, 50).padEnd(50)} [${task.priority}]`);
|
|
157
|
-
console.log(` ID: ${task.id}`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
console.log("");
|
|
161
|
-
await pressEnterToContinue();
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
console.error("\n Error fetching tasks:", error);
|
|
165
|
-
await pressEnterToContinue();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
async function createTask(config) {
|
|
169
|
-
try {
|
|
170
|
-
const title = await input({
|
|
171
|
-
message: "Task title:",
|
|
172
|
-
validate: (value) => (value.length > 0 ? true : "Title is required"),
|
|
173
|
-
});
|
|
174
|
-
const description = await input({
|
|
175
|
-
message: "Description (optional):",
|
|
176
|
-
});
|
|
177
|
-
const priority = await select({
|
|
178
|
-
message: "Priority:",
|
|
179
|
-
choices: [
|
|
180
|
-
{ name: "Low", value: "low" },
|
|
181
|
-
{ name: "Medium", value: "medium" },
|
|
182
|
-
{ name: "High", value: "high" },
|
|
183
|
-
],
|
|
184
|
-
default: "medium",
|
|
185
|
-
});
|
|
186
|
-
const res = await fetch(`${config.apiUrl}/api/tasks`, {
|
|
187
|
-
method: "POST",
|
|
188
|
-
headers: {
|
|
189
|
-
"Content-Type": "application/json",
|
|
190
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
191
|
-
},
|
|
192
|
-
body: JSON.stringify({
|
|
193
|
-
title,
|
|
194
|
-
description: description || undefined,
|
|
195
|
-
priority,
|
|
196
|
-
}),
|
|
197
|
-
});
|
|
198
|
-
if (!res.ok) {
|
|
199
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
200
|
-
await pressEnterToContinue();
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
const task = await res.json();
|
|
204
|
-
console.log(`\n Task created successfully!`);
|
|
205
|
-
console.log(` ID: ${task.id}`);
|
|
206
|
-
console.log(` Title: ${task.title}\n`);
|
|
207
|
-
await pressEnterToContinue();
|
|
208
|
-
}
|
|
209
|
-
catch (error) {
|
|
210
|
-
console.error("\n Error creating task:", error);
|
|
211
|
-
await pressEnterToContinue();
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
async function searchTasks(config) {
|
|
215
|
-
try {
|
|
216
|
-
const status = await select({
|
|
217
|
-
message: "Filter by status:",
|
|
218
|
-
choices: [
|
|
219
|
-
{ name: "Backlog", value: "backlog" },
|
|
220
|
-
{ name: "In Progress", value: "in_progress" },
|
|
221
|
-
{ name: "Review", value: "review" },
|
|
222
|
-
{ name: "Done", value: "done" },
|
|
223
|
-
],
|
|
224
|
-
});
|
|
225
|
-
const url = new URL("/api/tasks", config.apiUrl);
|
|
226
|
-
url.searchParams.set("status", status);
|
|
227
|
-
const res = await fetch(url.toString(), {
|
|
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
|
-
const tasks = await res.json();
|
|
236
|
-
console.log(`\n TASKS (${status.replace("_", " ").toUpperCase()})`);
|
|
237
|
-
console.log(" " + "-".repeat(70));
|
|
238
|
-
if (tasks.length === 0) {
|
|
239
|
-
console.log(" No tasks found with this status.");
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
for (const task of tasks) {
|
|
243
|
-
console.log(` - ${task.title.substring(0, 50)} [${task.priority}]`);
|
|
244
|
-
console.log(` ID: ${task.id}`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
console.log("");
|
|
248
|
-
await pressEnterToContinue();
|
|
249
|
-
}
|
|
250
|
-
catch (error) {
|
|
251
|
-
console.error("\n Error searching tasks:", error);
|
|
252
|
-
await pressEnterToContinue();
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
// ============================================
|
|
256
|
-
// PROJECTS MENU
|
|
257
|
-
// ============================================
|
|
258
|
-
async function projectsMenu() {
|
|
259
|
-
const config = ensureConfig();
|
|
260
|
-
const menuItems = [
|
|
261
|
-
{ name: "List all projects", value: "list" },
|
|
262
|
-
{ name: "Create new project", value: "create" },
|
|
263
|
-
{ name: "Back to main menu", value: "back" },
|
|
264
|
-
];
|
|
265
|
-
const choice = await select({
|
|
266
|
-
message: "Projects:",
|
|
267
|
-
choices: menuItems,
|
|
268
|
-
});
|
|
269
|
-
switch (choice) {
|
|
270
|
-
case "list":
|
|
271
|
-
await listProjects(config);
|
|
272
|
-
break;
|
|
273
|
-
case "create":
|
|
274
|
-
await createProject(config);
|
|
275
|
-
break;
|
|
276
|
-
case "back":
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
async function listProjects(config) {
|
|
281
|
-
try {
|
|
282
|
-
const res = await fetch(`${config.apiUrl}/api/projects`, {
|
|
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
|
-
const projects = await res.json();
|
|
291
|
-
console.log("\n PROJECTS");
|
|
292
|
-
console.log(" " + "-".repeat(70));
|
|
293
|
-
if (projects.length === 0) {
|
|
294
|
-
console.log(" No projects found.");
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
for (const project of projects) {
|
|
298
|
-
const statusIcon = project.status === "active" ? "[+]" : "[-]";
|
|
299
|
-
console.log(` ${statusIcon} ${project.name.substring(0, 40).padEnd(40)} [${project.workStatus}]`);
|
|
300
|
-
console.log(` ID: ${project.id}`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
console.log("");
|
|
304
|
-
await pressEnterToContinue();
|
|
305
|
-
}
|
|
306
|
-
catch (error) {
|
|
307
|
-
console.error("\n Error fetching projects:", error);
|
|
308
|
-
await pressEnterToContinue();
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
async function createProject(config) {
|
|
312
|
-
try {
|
|
313
|
-
const name = await input({
|
|
314
|
-
message: "Project name:",
|
|
315
|
-
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
316
|
-
});
|
|
317
|
-
const description = await input({
|
|
318
|
-
message: "Description (optional):",
|
|
319
|
-
});
|
|
320
|
-
const res = await fetch(`${config.apiUrl}/api/projects`, {
|
|
321
|
-
method: "POST",
|
|
322
|
-
headers: {
|
|
323
|
-
"Content-Type": "application/json",
|
|
324
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
325
|
-
},
|
|
326
|
-
body: JSON.stringify({
|
|
327
|
-
name,
|
|
328
|
-
description: description || undefined,
|
|
329
|
-
status: "active",
|
|
330
|
-
workStatus: "planning",
|
|
331
|
-
}),
|
|
332
|
-
});
|
|
333
|
-
if (!res.ok) {
|
|
334
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
335
|
-
await pressEnterToContinue();
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
const project = await res.json();
|
|
339
|
-
console.log(`\n Project created successfully!`);
|
|
340
|
-
console.log(` ID: ${project.id}`);
|
|
341
|
-
console.log(` Name: ${project.name}\n`);
|
|
342
|
-
await pressEnterToContinue();
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
console.error("\n Error creating project:", error);
|
|
346
|
-
await pressEnterToContinue();
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
// ============================================
|
|
350
|
-
// ROADMAPS MENU
|
|
351
|
-
// ============================================
|
|
352
|
-
async function roadmapsMenu() {
|
|
353
|
-
const config = ensureConfig();
|
|
354
|
-
const menuItems = [
|
|
355
|
-
{ name: "List all roadmaps", value: "list" },
|
|
356
|
-
{ name: "Create new roadmap", value: "create" },
|
|
357
|
-
{ name: "Generate with AI", value: "generate" },
|
|
358
|
-
{ name: "Back to main menu", value: "back" },
|
|
359
|
-
];
|
|
360
|
-
const choice = await select({
|
|
361
|
-
message: "Roadmaps:",
|
|
362
|
-
choices: menuItems,
|
|
363
|
-
});
|
|
364
|
-
switch (choice) {
|
|
365
|
-
case "list":
|
|
366
|
-
await listRoadmaps(config);
|
|
367
|
-
break;
|
|
368
|
-
case "create":
|
|
369
|
-
await createRoadmap(config);
|
|
370
|
-
break;
|
|
371
|
-
case "generate":
|
|
372
|
-
await generateRoadmap(config);
|
|
373
|
-
break;
|
|
374
|
-
case "back":
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
async function listRoadmaps(config) {
|
|
379
|
-
try {
|
|
380
|
-
const res = await fetch(`${config.apiUrl}/api/roadmaps`, {
|
|
381
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
382
|
-
});
|
|
383
|
-
if (!res.ok) {
|
|
384
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
385
|
-
await pressEnterToContinue();
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
const roadmaps = await res.json();
|
|
389
|
-
console.log("\n ROADMAPS");
|
|
390
|
-
console.log(" " + "-".repeat(70));
|
|
391
|
-
if (roadmaps.length === 0) {
|
|
392
|
-
console.log(" No roadmaps found.");
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
for (const roadmap of roadmaps) {
|
|
396
|
-
const typeLabel = roadmap.type === "global" ? "[Global]" : "[Project]";
|
|
397
|
-
console.log(` ${typeLabel.padEnd(10)} ${roadmap.name.substring(0, 40)}`);
|
|
398
|
-
console.log(` ID: ${roadmap.id}`);
|
|
399
|
-
console.log(` ${roadmap.phases?.length || 0} phases, ${roadmap.features?.length || 0} features`);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
console.log("");
|
|
403
|
-
await pressEnterToContinue();
|
|
404
|
-
}
|
|
405
|
-
catch (error) {
|
|
406
|
-
console.error("\n Error fetching roadmaps:", error);
|
|
407
|
-
await pressEnterToContinue();
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
async function createRoadmap(config) {
|
|
411
|
-
try {
|
|
412
|
-
const name = await input({
|
|
413
|
-
message: "Roadmap name:",
|
|
414
|
-
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
415
|
-
});
|
|
416
|
-
const type = await select({
|
|
417
|
-
message: "Type:",
|
|
418
|
-
choices: [
|
|
419
|
-
{ name: "Global", value: "global" },
|
|
420
|
-
{ name: "Project", value: "project" },
|
|
421
|
-
],
|
|
422
|
-
default: "global",
|
|
423
|
-
});
|
|
424
|
-
const vision = await input({
|
|
425
|
-
message: "Vision (optional):",
|
|
426
|
-
});
|
|
427
|
-
const res = await fetch(`${config.apiUrl}/api/roadmaps`, {
|
|
428
|
-
method: "POST",
|
|
429
|
-
headers: {
|
|
430
|
-
"Content-Type": "application/json",
|
|
431
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
432
|
-
},
|
|
433
|
-
body: JSON.stringify({
|
|
434
|
-
name,
|
|
435
|
-
type,
|
|
436
|
-
vision: vision || undefined,
|
|
437
|
-
targetAudience: {
|
|
438
|
-
primary: "All users",
|
|
439
|
-
secondary: [],
|
|
440
|
-
},
|
|
441
|
-
}),
|
|
442
|
-
});
|
|
443
|
-
if (!res.ok) {
|
|
444
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
445
|
-
await pressEnterToContinue();
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
const roadmap = await res.json();
|
|
449
|
-
console.log(`\n Roadmap created successfully!`);
|
|
450
|
-
console.log(` ID: ${roadmap.id}`);
|
|
451
|
-
console.log(` Name: ${roadmap.name}\n`);
|
|
452
|
-
await pressEnterToContinue();
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
console.error("\n Error creating roadmap:", error);
|
|
456
|
-
await pressEnterToContinue();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
async function generateRoadmap(config) {
|
|
460
|
-
try {
|
|
461
|
-
const roadmapId = await input({
|
|
462
|
-
message: "Roadmap ID to generate for:",
|
|
463
|
-
validate: (value) => (value.length > 0 ? true : "Roadmap ID is required"),
|
|
464
|
-
});
|
|
465
|
-
const context = await input({
|
|
466
|
-
message: "Additional context (optional):",
|
|
467
|
-
});
|
|
468
|
-
console.log("\n Generating roadmap with AI... This may take a moment.\n");
|
|
469
|
-
const res = await fetch(`${config.apiUrl}/api/roadmaps/generate`, {
|
|
470
|
-
method: "POST",
|
|
471
|
-
headers: {
|
|
472
|
-
"Content-Type": "application/json",
|
|
473
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
474
|
-
},
|
|
475
|
-
body: JSON.stringify({
|
|
476
|
-
roadmapId,
|
|
477
|
-
additionalContext: context || undefined,
|
|
478
|
-
}),
|
|
479
|
-
});
|
|
480
|
-
if (!res.ok) {
|
|
481
|
-
const error = await res.json().catch(() => ({}));
|
|
482
|
-
console.error(`\n Error: ${error.error || `API returned ${res.status}`}\n`);
|
|
483
|
-
await pressEnterToContinue();
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
const result = await res.json();
|
|
487
|
-
console.log(` Roadmap generated successfully!`);
|
|
488
|
-
console.log(` Phases created: ${result.phasesGenerated}`);
|
|
489
|
-
console.log(` Features created: ${result.featuresGenerated}\n`);
|
|
490
|
-
await pressEnterToContinue();
|
|
491
|
-
}
|
|
492
|
-
catch (error) {
|
|
493
|
-
console.error("\n Error generating roadmap:", error);
|
|
494
|
-
await pressEnterToContinue();
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
// ============================================
|
|
498
|
-
// WORKFLOWS MENU
|
|
499
|
-
// ============================================
|
|
500
|
-
async function workflowsMenu() {
|
|
501
|
-
const config = ensureConfig();
|
|
502
|
-
const menuItems = [
|
|
503
|
-
{ name: "List all workflows", value: "list" },
|
|
504
|
-
{ name: "Create new workflow", value: "create" },
|
|
505
|
-
{ name: "Back to main menu", value: "back" },
|
|
506
|
-
];
|
|
507
|
-
const choice = await select({
|
|
508
|
-
message: "Workflows:",
|
|
509
|
-
choices: menuItems,
|
|
510
|
-
});
|
|
511
|
-
switch (choice) {
|
|
512
|
-
case "list":
|
|
513
|
-
await listWorkflows(config);
|
|
514
|
-
break;
|
|
515
|
-
case "create":
|
|
516
|
-
await createWorkflow(config);
|
|
517
|
-
break;
|
|
518
|
-
case "back":
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
async function listWorkflows(config) {
|
|
523
|
-
try {
|
|
524
|
-
const res = await fetch(`${config.apiUrl}/api/workflows`, {
|
|
525
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
526
|
-
});
|
|
527
|
-
if (!res.ok) {
|
|
528
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
529
|
-
await pressEnterToContinue();
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
const workflows = await res.json();
|
|
533
|
-
console.log("\n WORKFLOWS");
|
|
534
|
-
console.log(" " + "-".repeat(70));
|
|
535
|
-
if (workflows.length === 0) {
|
|
536
|
-
console.log(" No workflows found.");
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
for (const workflow of workflows) {
|
|
540
|
-
console.log(` - ${workflow.name.substring(0, 40).padEnd(40)} [${workflow.status}]`);
|
|
541
|
-
console.log(` ID: ${workflow.id}`);
|
|
542
|
-
console.log(` Value Stream: ${workflow.valueStream.replace(/_/g, " ")}`);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
console.log("");
|
|
546
|
-
await pressEnterToContinue();
|
|
547
|
-
}
|
|
548
|
-
catch (error) {
|
|
549
|
-
console.error("\n Error fetching workflows:", error);
|
|
550
|
-
await pressEnterToContinue();
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
async function createWorkflow(config) {
|
|
554
|
-
try {
|
|
555
|
-
const name = await input({
|
|
556
|
-
message: "Workflow name:",
|
|
557
|
-
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
558
|
-
});
|
|
559
|
-
const description = await input({
|
|
560
|
-
message: "Description (optional):",
|
|
561
|
-
});
|
|
562
|
-
const valueStream = await select({
|
|
563
|
-
message: "Value stream:",
|
|
564
|
-
choices: [
|
|
565
|
-
{ name: "Order to Delivery", value: "order_to_delivery" },
|
|
566
|
-
{ name: "Procure to Pay", value: "procure_to_pay" },
|
|
567
|
-
{ name: "Returns Management", value: "returns_management" },
|
|
568
|
-
{ name: "Product Lifecycle", value: "product_lifecycle" },
|
|
569
|
-
{ name: "Customer Service", value: "customer_service" },
|
|
570
|
-
{ name: "Marketing & Sales", value: "marketing_sales" },
|
|
571
|
-
{ name: "Finance & Accounting", value: "finance_accounting" },
|
|
572
|
-
{ name: "HR Operations", value: "hr_operations" },
|
|
573
|
-
{ name: "IT Operations", value: "it_operations" },
|
|
574
|
-
{ name: "General", value: "general" },
|
|
575
|
-
],
|
|
576
|
-
default: "general",
|
|
577
|
-
});
|
|
578
|
-
const res = await fetch(`${config.apiUrl}/api/workflows`, {
|
|
579
|
-
method: "POST",
|
|
580
|
-
headers: {
|
|
581
|
-
"Content-Type": "application/json",
|
|
582
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
583
|
-
},
|
|
584
|
-
body: JSON.stringify({
|
|
585
|
-
name,
|
|
586
|
-
description: description || "",
|
|
587
|
-
valueStream,
|
|
588
|
-
status: "draft",
|
|
589
|
-
action: "manual",
|
|
590
|
-
departmentIds: [],
|
|
591
|
-
autonomyWeight: 0,
|
|
592
|
-
}),
|
|
593
|
-
});
|
|
594
|
-
if (!res.ok) {
|
|
595
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
596
|
-
await pressEnterToContinue();
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
const workflow = await res.json();
|
|
600
|
-
console.log(`\n Workflow created successfully!`);
|
|
601
|
-
console.log(` ID: ${workflow.id}`);
|
|
602
|
-
console.log(` Name: ${workflow.name}\n`);
|
|
603
|
-
await pressEnterToContinue();
|
|
604
|
-
}
|
|
605
|
-
catch (error) {
|
|
606
|
-
console.error("\n Error creating workflow:", error);
|
|
607
|
-
await pressEnterToContinue();
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// ============================================
|
|
611
|
-
// IDEAS MENU
|
|
612
|
-
// ============================================
|
|
613
|
-
async function ideasMenu() {
|
|
614
|
-
const config = ensureConfig();
|
|
615
|
-
const menuItems = [
|
|
616
|
-
{ name: "List all ideas", value: "list" },
|
|
617
|
-
{ name: "Create new idea", value: "create" },
|
|
618
|
-
{ name: "Back to main menu", value: "back" },
|
|
619
|
-
];
|
|
620
|
-
const choice = await select({
|
|
621
|
-
message: "Ideas:",
|
|
622
|
-
choices: menuItems,
|
|
623
|
-
});
|
|
624
|
-
switch (choice) {
|
|
625
|
-
case "list":
|
|
626
|
-
await listIdeas(config);
|
|
627
|
-
break;
|
|
628
|
-
case "create":
|
|
629
|
-
await createIdea(config);
|
|
630
|
-
break;
|
|
631
|
-
case "back":
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
async function listIdeas(config) {
|
|
636
|
-
try {
|
|
637
|
-
const res = await fetch(`${config.apiUrl}/api/ideas`, {
|
|
638
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
639
|
-
});
|
|
640
|
-
if (!res.ok) {
|
|
641
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
642
|
-
await pressEnterToContinue();
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
const ideas = await res.json();
|
|
646
|
-
console.log("\n IDEAS");
|
|
647
|
-
console.log(" " + "-".repeat(70));
|
|
648
|
-
if (ideas.length === 0) {
|
|
649
|
-
console.log(" No ideas found.");
|
|
650
|
-
}
|
|
651
|
-
else {
|
|
652
|
-
for (const idea of ideas) {
|
|
653
|
-
const statusIcon = idea.status === "active" ? "[o]" : idea.status === "converted" ? "[>]" : "[.]";
|
|
654
|
-
console.log(` ${statusIcon} ${idea.title.substring(0, 50).padEnd(50)} [${idea.status}]`);
|
|
655
|
-
console.log(` ID: ${idea.id}`);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
console.log("");
|
|
659
|
-
await pressEnterToContinue();
|
|
660
|
-
}
|
|
661
|
-
catch (error) {
|
|
662
|
-
console.error("\n Error fetching ideas:", error);
|
|
663
|
-
await pressEnterToContinue();
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
async function createIdea(config) {
|
|
667
|
-
try {
|
|
668
|
-
const title = await input({
|
|
669
|
-
message: "Idea title:",
|
|
670
|
-
validate: (value) => (value.length > 0 ? true : "Title is required"),
|
|
671
|
-
});
|
|
672
|
-
const description = await input({
|
|
673
|
-
message: "Description (optional):",
|
|
674
|
-
});
|
|
675
|
-
const category = await input({
|
|
676
|
-
message: "Category (optional):",
|
|
677
|
-
});
|
|
678
|
-
const res = await fetch(`${config.apiUrl}/api/ideas`, {
|
|
679
|
-
method: "POST",
|
|
680
|
-
headers: {
|
|
681
|
-
"Content-Type": "application/json",
|
|
682
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
683
|
-
},
|
|
684
|
-
body: JSON.stringify({
|
|
685
|
-
title,
|
|
686
|
-
description: description || undefined,
|
|
687
|
-
category: category || undefined,
|
|
688
|
-
status: "draft",
|
|
689
|
-
}),
|
|
690
|
-
});
|
|
691
|
-
if (!res.ok) {
|
|
692
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
693
|
-
await pressEnterToContinue();
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
const idea = await res.json();
|
|
697
|
-
console.log(`\n Idea created successfully!`);
|
|
698
|
-
console.log(` ID: ${idea.id}`);
|
|
699
|
-
console.log(` Title: ${idea.title}\n`);
|
|
700
|
-
await pressEnterToContinue();
|
|
701
|
-
}
|
|
702
|
-
catch (error) {
|
|
703
|
-
console.error("\n Error creating idea:", error);
|
|
704
|
-
await pressEnterToContinue();
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
// ============================================
|
|
708
|
-
// VM SESSIONS MENU
|
|
709
|
-
// ============================================
|
|
710
|
-
async function vmSessionsMenu() {
|
|
711
|
-
const config = ensureConfig();
|
|
712
|
-
const menuItems = [
|
|
713
|
-
{ name: "List all VM sessions", value: "list" },
|
|
714
|
-
{ name: "Create new VM session", value: "create" },
|
|
715
|
-
{ name: "Back to main menu", value: "back" },
|
|
716
|
-
];
|
|
717
|
-
const choice = await select({
|
|
718
|
-
message: "VM Sessions:",
|
|
719
|
-
choices: menuItems,
|
|
720
|
-
});
|
|
721
|
-
switch (choice) {
|
|
722
|
-
case "list":
|
|
723
|
-
await listVMSessions(config);
|
|
724
|
-
break;
|
|
725
|
-
case "create":
|
|
726
|
-
await createVMSession(config);
|
|
727
|
-
break;
|
|
728
|
-
case "back":
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
async function listVMSessions(config) {
|
|
733
|
-
try {
|
|
734
|
-
const res = await fetch(`${config.apiUrl}/api/vm-sessions`, {
|
|
735
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
736
|
-
});
|
|
737
|
-
if (!res.ok) {
|
|
738
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
739
|
-
await pressEnterToContinue();
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
const data = await res.json();
|
|
743
|
-
const sessions = data.sessions || [];
|
|
744
|
-
console.log("\n VM SESSIONS");
|
|
745
|
-
console.log(" " + "-".repeat(70));
|
|
746
|
-
if (sessions.length === 0) {
|
|
747
|
-
console.log(" No VM sessions found.");
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
for (const session of sessions) {
|
|
751
|
-
const statusLabel = session.vmStatus.replace(/_/g, " ").toUpperCase();
|
|
752
|
-
console.log(` - ${session.name.substring(0, 40).padEnd(40)} [${statusLabel}]`);
|
|
753
|
-
console.log(` ID: ${session.id}`);
|
|
754
|
-
console.log(` Agent: ${session.agentType}`);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
console.log("");
|
|
758
|
-
await pressEnterToContinue();
|
|
759
|
-
}
|
|
760
|
-
catch (error) {
|
|
761
|
-
console.error("\n Error fetching VM sessions:", error);
|
|
762
|
-
await pressEnterToContinue();
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
async function createVMSession(config) {
|
|
766
|
-
try {
|
|
767
|
-
const name = await input({
|
|
768
|
-
message: "Session name:",
|
|
769
|
-
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
770
|
-
});
|
|
771
|
-
const prompt = await input({
|
|
772
|
-
message: "Prompt (task description):",
|
|
773
|
-
validate: (value) => (value.length > 0 ? true : "Prompt is required"),
|
|
774
|
-
});
|
|
775
|
-
const agentType = await select({
|
|
776
|
-
message: "Agent type:",
|
|
777
|
-
choices: [
|
|
778
|
-
{ name: "Claude Code", value: "claude-code" },
|
|
779
|
-
{ name: "Gemini CLI", value: "gemini-cli" },
|
|
780
|
-
{ name: "Aider", value: "aider" },
|
|
781
|
-
{ name: "Custom", value: "custom" },
|
|
782
|
-
],
|
|
783
|
-
default: "claude-code",
|
|
784
|
-
});
|
|
785
|
-
const res = await fetch(`${config.apiUrl}/api/vm-sessions`, {
|
|
786
|
-
method: "POST",
|
|
787
|
-
headers: {
|
|
788
|
-
"Content-Type": "application/json",
|
|
789
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
790
|
-
},
|
|
791
|
-
body: JSON.stringify({
|
|
792
|
-
name,
|
|
793
|
-
prompt,
|
|
794
|
-
agentType,
|
|
795
|
-
machineType: "e2-medium",
|
|
796
|
-
zone: "us-central1-a",
|
|
797
|
-
startTrigger: "manual",
|
|
798
|
-
}),
|
|
799
|
-
});
|
|
800
|
-
if (!res.ok) {
|
|
801
|
-
const errorData = await res.json().catch(() => ({}));
|
|
802
|
-
console.error(`\n Error: ${errorData.error || `API returned ${res.status}`}\n`);
|
|
803
|
-
await pressEnterToContinue();
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
const session = await res.json();
|
|
807
|
-
console.log(`\n VM session created successfully!`);
|
|
808
|
-
console.log(` ID: ${session.id}`);
|
|
809
|
-
console.log(` Name: ${session.name}`);
|
|
810
|
-
console.log(` VM Name: ${session.vmName}`);
|
|
811
|
-
console.log(`\n To start the VM, run: husky vm start ${session.id}\n`);
|
|
812
|
-
await pressEnterToContinue();
|
|
813
|
-
}
|
|
814
|
-
catch (error) {
|
|
815
|
-
console.error("\n Error creating VM session:", error);
|
|
816
|
-
await pressEnterToContinue();
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
// ============================================
|
|
820
|
-
// JULES SESSIONS MENU
|
|
821
|
-
// ============================================
|
|
822
|
-
async function julesSessionsMenu() {
|
|
823
|
-
const config = ensureConfig();
|
|
824
|
-
const menuItems = [
|
|
825
|
-
{ name: "List all Jules sessions", value: "list" },
|
|
826
|
-
{ name: "Create new Jules session", value: "create" },
|
|
827
|
-
{ name: "List available sources", value: "sources" },
|
|
828
|
-
{ name: "Back to main menu", value: "back" },
|
|
829
|
-
];
|
|
830
|
-
const choice = await select({
|
|
831
|
-
message: "Jules Sessions:",
|
|
832
|
-
choices: menuItems,
|
|
833
|
-
});
|
|
834
|
-
switch (choice) {
|
|
835
|
-
case "list":
|
|
836
|
-
await listJulesSessions(config);
|
|
837
|
-
break;
|
|
838
|
-
case "create":
|
|
839
|
-
await createJulesSession(config);
|
|
840
|
-
break;
|
|
841
|
-
case "sources":
|
|
842
|
-
await listJulesSources(config);
|
|
843
|
-
break;
|
|
844
|
-
case "back":
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
async function listJulesSessions(config) {
|
|
849
|
-
try {
|
|
850
|
-
const res = await fetch(`${config.apiUrl}/api/jules-sessions`, {
|
|
851
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
852
|
-
});
|
|
853
|
-
if (!res.ok) {
|
|
854
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
855
|
-
await pressEnterToContinue();
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
const data = await res.json();
|
|
859
|
-
const sessions = data.sessions || [];
|
|
860
|
-
console.log("\n JULES SESSIONS");
|
|
861
|
-
console.log(" " + "-".repeat(70));
|
|
862
|
-
if (sessions.length === 0) {
|
|
863
|
-
console.log(" No Jules sessions found.");
|
|
864
|
-
}
|
|
865
|
-
else {
|
|
866
|
-
for (const session of sessions) {
|
|
867
|
-
const statusLabel = session.status.replace(/_/g, " ");
|
|
868
|
-
console.log(` - ${session.name.substring(0, 40).padEnd(40)} [${statusLabel}]`);
|
|
869
|
-
console.log(` ID: ${session.id}`);
|
|
870
|
-
console.log(` Source: ${session.sourceName || session.sourceId}`);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
console.log("");
|
|
874
|
-
await pressEnterToContinue();
|
|
875
|
-
}
|
|
876
|
-
catch (error) {
|
|
877
|
-
console.error("\n Error fetching Jules sessions:", error);
|
|
878
|
-
await pressEnterToContinue();
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
async function createJulesSession(config) {
|
|
882
|
-
try {
|
|
883
|
-
const name = await input({
|
|
884
|
-
message: "Session name:",
|
|
885
|
-
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
886
|
-
});
|
|
887
|
-
const sourceId = await input({
|
|
888
|
-
message: "Source ID (run 'jules sources' to see available):",
|
|
889
|
-
validate: (value) => (value.length > 0 ? true : "Source ID is required"),
|
|
890
|
-
});
|
|
891
|
-
const prompt = await input({
|
|
892
|
-
message: "Prompt (what should Jules do):",
|
|
893
|
-
validate: (value) => (value.length > 0 ? true : "Prompt is required"),
|
|
894
|
-
});
|
|
895
|
-
const requireApproval = await confirm({
|
|
896
|
-
message: "Require plan approval?",
|
|
897
|
-
default: true,
|
|
898
|
-
});
|
|
899
|
-
const res = await fetch(`${config.apiUrl}/api/jules-sessions`, {
|
|
900
|
-
method: "POST",
|
|
901
|
-
headers: {
|
|
902
|
-
"Content-Type": "application/json",
|
|
903
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
904
|
-
},
|
|
905
|
-
body: JSON.stringify({
|
|
906
|
-
name,
|
|
907
|
-
prompt,
|
|
908
|
-
sourceId,
|
|
909
|
-
requirePlanApproval: requireApproval,
|
|
910
|
-
}),
|
|
911
|
-
});
|
|
912
|
-
if (!res.ok) {
|
|
913
|
-
const errorData = await res.json().catch(() => ({}));
|
|
914
|
-
console.error(`\n Error: ${errorData.error || `API returned ${res.status}`}\n`);
|
|
915
|
-
await pressEnterToContinue();
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
const session = await res.json();
|
|
919
|
-
console.log(`\n Jules session created successfully!`);
|
|
920
|
-
console.log(` ID: ${session.id}`);
|
|
921
|
-
console.log(` Name: ${session.name}`);
|
|
922
|
-
console.log(` Jules Session ID: ${session.julesSessionId}\n`);
|
|
923
|
-
await pressEnterToContinue();
|
|
924
|
-
}
|
|
925
|
-
catch (error) {
|
|
926
|
-
console.error("\n Error creating Jules session:", error);
|
|
927
|
-
await pressEnterToContinue();
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
async function listJulesSources(config) {
|
|
931
|
-
try {
|
|
932
|
-
const res = await fetch(`${config.apiUrl}/api/jules-sessions/sources`, {
|
|
933
|
-
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
934
|
-
});
|
|
935
|
-
if (!res.ok) {
|
|
936
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
937
|
-
await pressEnterToContinue();
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
const data = await res.json();
|
|
941
|
-
if (!data.configured) {
|
|
942
|
-
console.log("\n Jules API is not configured. Add JULES_API_KEY to Secret Manager.\n");
|
|
943
|
-
await pressEnterToContinue();
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
const sources = data.sources || [];
|
|
947
|
-
console.log("\n JULES SOURCES (GitHub Repositories)");
|
|
948
|
-
console.log(" " + "-".repeat(70));
|
|
949
|
-
if (sources.length === 0) {
|
|
950
|
-
console.log(" No sources configured.");
|
|
951
|
-
}
|
|
952
|
-
else {
|
|
953
|
-
for (const source of sources) {
|
|
954
|
-
const connected = source.connected ? "Yes" : "No";
|
|
955
|
-
console.log(` - ${source.fullName.substring(0, 40).padEnd(40)} [${source.defaultBranch}]`);
|
|
956
|
-
console.log(` ID: ${source.id}`);
|
|
957
|
-
console.log(` Connected: ${connected}`);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
console.log("");
|
|
961
|
-
await pressEnterToContinue();
|
|
962
|
-
}
|
|
963
|
-
catch (error) {
|
|
964
|
-
console.error("\n Error fetching Jules sources:", error);
|
|
965
|
-
await pressEnterToContinue();
|
|
966
|
-
}
|
|
967
|
-
}
|
|
1
|
+
import { select, input } from "@inquirer/prompts";
|
|
2
|
+
import { getConfig, setConfig } from "./config.js";
|
|
3
|
+
// Import modular menus
|
|
4
|
+
import { tasksMenu } from "./interactive/tasks.js";
|
|
5
|
+
import { projectsMenu } from "./interactive/projects.js";
|
|
6
|
+
import { ideasMenu } from "./interactive/ideas.js";
|
|
7
|
+
import { departmentsMenu } from "./interactive/departments.js";
|
|
8
|
+
import { processesMenu } from "./interactive/processes.js";
|
|
9
|
+
import { workflowsMenu } from "./interactive/workflows.js";
|
|
10
|
+
import { vmSessionsMenu } from "./interactive/vm-sessions.js";
|
|
11
|
+
import { julesSessionsMenu } from "./interactive/jules-sessions.js";
|
|
12
|
+
import { roadmapsMenu } from "./interactive/roadmaps.js";
|
|
13
|
+
import { strategyMenu } from "./interactive/strategy.js";
|
|
14
|
+
import { changelogMenu } from "./interactive/changelog.js";
|
|
15
|
+
import { worktreesMenu } from "./interactive/worktrees.js";
|
|
16
|
+
import { clearScreen, printHeader, pressEnterToContinue } from "./interactive/utils.js";
|
|
968
17
|
// ============================================
|
|
969
|
-
//
|
|
18
|
+
// MAIN MENU
|
|
970
19
|
// ============================================
|
|
971
|
-
async function
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
20
|
+
export async function runInteractiveMode() {
|
|
21
|
+
let running = true;
|
|
22
|
+
while (running) {
|
|
23
|
+
clearScreen();
|
|
24
|
+
printHeader();
|
|
25
|
+
const mainMenuItems = [
|
|
26
|
+
{ name: "Tasks", value: "tasks", description: "Manage tasks" },
|
|
27
|
+
{ name: "Projects", value: "projects", description: "Manage projects" },
|
|
28
|
+
{ name: "Ideas", value: "ideas", description: "Manage ideas" },
|
|
29
|
+
{ name: "---", value: "separator1", description: "" },
|
|
30
|
+
{ name: "Roadmaps", value: "roadmaps", description: "Manage roadmaps" },
|
|
31
|
+
{ name: "Workflows", value: "workflows", description: "Manage workflows" },
|
|
32
|
+
{ name: "Departments", value: "departments", description: "Manage departments" },
|
|
33
|
+
{ name: "Processes", value: "processes", description: "Manage processes" },
|
|
34
|
+
{ name: "---", value: "separator2", description: "" },
|
|
35
|
+
{ name: "VM Sessions", value: "vm", description: "Manage VM sessions" },
|
|
36
|
+
{ name: "Jules Sessions", value: "jules", description: "Manage Jules AI sessions" },
|
|
37
|
+
{ name: "Worktrees", value: "worktrees", description: "Manage Git worktrees for agent isolation" },
|
|
38
|
+
{ name: "---", value: "separator3", description: "" },
|
|
39
|
+
{ name: "Business Strategy", value: "strategy", description: "Manage business strategy" },
|
|
40
|
+
{ name: "Changelog", value: "changelog", description: "Generate and manage changelogs" },
|
|
41
|
+
{ name: "Dashboard Settings", value: "settings", description: "Manage dashboard settings" },
|
|
42
|
+
{ name: "CLI Config", value: "config", description: "Configure CLI (API URL, API Key)" },
|
|
43
|
+
{ name: "---", value: "separator4", description: "" },
|
|
44
|
+
{ name: "Exit", value: "exit", description: "Exit interactive mode" },
|
|
45
|
+
];
|
|
46
|
+
try {
|
|
47
|
+
const choice = await select({
|
|
48
|
+
message: "Select an option:",
|
|
49
|
+
choices: mainMenuItems.filter((item) => !item.value.startsWith("separator")),
|
|
50
|
+
});
|
|
51
|
+
switch (choice) {
|
|
52
|
+
case "tasks":
|
|
53
|
+
await tasksMenu();
|
|
54
|
+
break;
|
|
55
|
+
case "projects":
|
|
56
|
+
await projectsMenu();
|
|
57
|
+
break;
|
|
58
|
+
case "ideas":
|
|
59
|
+
await ideasMenu();
|
|
60
|
+
break;
|
|
61
|
+
case "roadmaps":
|
|
62
|
+
await roadmapsMenu();
|
|
63
|
+
break;
|
|
64
|
+
case "workflows":
|
|
65
|
+
await workflowsMenu();
|
|
66
|
+
break;
|
|
67
|
+
case "departments":
|
|
68
|
+
await departmentsMenu();
|
|
69
|
+
break;
|
|
70
|
+
case "processes":
|
|
71
|
+
await processesMenu();
|
|
72
|
+
break;
|
|
73
|
+
case "vm":
|
|
74
|
+
await vmSessionsMenu();
|
|
75
|
+
break;
|
|
76
|
+
case "jules":
|
|
77
|
+
await julesSessionsMenu();
|
|
78
|
+
break;
|
|
79
|
+
case "worktrees":
|
|
80
|
+
await worktreesMenu();
|
|
81
|
+
break;
|
|
82
|
+
case "strategy":
|
|
83
|
+
await strategyMenu();
|
|
84
|
+
break;
|
|
85
|
+
case "changelog":
|
|
86
|
+
await changelogMenu();
|
|
87
|
+
break;
|
|
88
|
+
case "settings":
|
|
89
|
+
await settingsMenu();
|
|
90
|
+
break;
|
|
91
|
+
case "config":
|
|
92
|
+
await cliConfigMenu();
|
|
93
|
+
break;
|
|
94
|
+
case "exit":
|
|
95
|
+
running = false;
|
|
96
|
+
console.log("\n Goodbye!\n");
|
|
97
|
+
break;
|
|
1032
98
|
}
|
|
1033
99
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
else {
|
|
1040
|
-
for (const goal of strategy.goals) {
|
|
1041
|
-
const statusIcon = goal.status === "completed" ? "[x]" : goal.status === "in_progress" ? "[>]" : "[ ]";
|
|
1042
|
-
console.log(` ${statusIcon} ${goal.title} (${goal.progress}%)`);
|
|
100
|
+
catch (error) {
|
|
101
|
+
// User pressed Ctrl+C or escaped
|
|
102
|
+
if (error.name === "ExitPromptError") {
|
|
103
|
+
running = false;
|
|
104
|
+
console.log("\n Goodbye!\n");
|
|
1043
105
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
console.log(" " + "-".repeat(40));
|
|
1047
|
-
if (strategy.targetAudience.length === 0) {
|
|
1048
|
-
console.log(" (none)");
|
|
1049
|
-
}
|
|
1050
|
-
else {
|
|
1051
|
-
for (const persona of strategy.targetAudience) {
|
|
1052
|
-
console.log(` - ${persona.name}`);
|
|
106
|
+
else {
|
|
107
|
+
throw error;
|
|
1053
108
|
}
|
|
1054
109
|
}
|
|
1055
|
-
console.log("");
|
|
1056
|
-
await pressEnterToContinue();
|
|
1057
|
-
}
|
|
1058
|
-
catch (error) {
|
|
1059
|
-
console.error("\n Error fetching strategy:", error);
|
|
1060
|
-
await pressEnterToContinue();
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
async function updateVision(config) {
|
|
1064
|
-
try {
|
|
1065
|
-
const vision = await input({
|
|
1066
|
-
message: "New vision statement:",
|
|
1067
|
-
validate: (value) => (value.length > 0 ? true : "Vision is required"),
|
|
1068
|
-
});
|
|
1069
|
-
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
1070
|
-
method: "PATCH",
|
|
1071
|
-
headers: {
|
|
1072
|
-
"Content-Type": "application/json",
|
|
1073
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
1074
|
-
},
|
|
1075
|
-
body: JSON.stringify({ vision }),
|
|
1076
|
-
});
|
|
1077
|
-
if (!res.ok) {
|
|
1078
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
1079
|
-
await pressEnterToContinue();
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
console.log(`\n Vision updated successfully!\n`);
|
|
1083
|
-
await pressEnterToContinue();
|
|
1084
|
-
}
|
|
1085
|
-
catch (error) {
|
|
1086
|
-
console.error("\n Error updating vision:", error);
|
|
1087
|
-
await pressEnterToContinue();
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
async function updateMission(config) {
|
|
1091
|
-
try {
|
|
1092
|
-
const mission = await input({
|
|
1093
|
-
message: "New mission statement:",
|
|
1094
|
-
validate: (value) => (value.length > 0 ? true : "Mission is required"),
|
|
1095
|
-
});
|
|
1096
|
-
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
1097
|
-
method: "PATCH",
|
|
1098
|
-
headers: {
|
|
1099
|
-
"Content-Type": "application/json",
|
|
1100
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
1101
|
-
},
|
|
1102
|
-
body: JSON.stringify({ mission }),
|
|
1103
|
-
});
|
|
1104
|
-
if (!res.ok) {
|
|
1105
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
1106
|
-
await pressEnterToContinue();
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
console.log(`\n Mission updated successfully!\n`);
|
|
1110
|
-
await pressEnterToContinue();
|
|
1111
|
-
}
|
|
1112
|
-
catch (error) {
|
|
1113
|
-
console.error("\n Error updating mission:", error);
|
|
1114
|
-
await pressEnterToContinue();
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
async function addValue(config) {
|
|
1118
|
-
try {
|
|
1119
|
-
const name = await input({
|
|
1120
|
-
message: "Value name:",
|
|
1121
|
-
validate: (value) => (value.length > 0 ? true : "Name is required"),
|
|
1122
|
-
});
|
|
1123
|
-
const description = await input({
|
|
1124
|
-
message: "Description (optional):",
|
|
1125
|
-
});
|
|
1126
|
-
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
1127
|
-
method: "POST",
|
|
1128
|
-
headers: {
|
|
1129
|
-
"Content-Type": "application/json",
|
|
1130
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
1131
|
-
},
|
|
1132
|
-
body: JSON.stringify({
|
|
1133
|
-
type: "value",
|
|
1134
|
-
name,
|
|
1135
|
-
description: description || "",
|
|
1136
|
-
}),
|
|
1137
|
-
});
|
|
1138
|
-
if (!res.ok) {
|
|
1139
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
1140
|
-
await pressEnterToContinue();
|
|
1141
|
-
return;
|
|
1142
|
-
}
|
|
1143
|
-
console.log(`\n Value added successfully!\n`);
|
|
1144
|
-
await pressEnterToContinue();
|
|
1145
|
-
}
|
|
1146
|
-
catch (error) {
|
|
1147
|
-
console.error("\n Error adding value:", error);
|
|
1148
|
-
await pressEnterToContinue();
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
async function addGoal(config) {
|
|
1152
|
-
try {
|
|
1153
|
-
const title = await input({
|
|
1154
|
-
message: "Goal title:",
|
|
1155
|
-
validate: (value) => (value.length > 0 ? true : "Title is required"),
|
|
1156
|
-
});
|
|
1157
|
-
const description = await input({
|
|
1158
|
-
message: "Description (optional):",
|
|
1159
|
-
});
|
|
1160
|
-
const status = await select({
|
|
1161
|
-
message: "Status:",
|
|
1162
|
-
choices: [
|
|
1163
|
-
{ name: "Not Started", value: "not_started" },
|
|
1164
|
-
{ name: "In Progress", value: "in_progress" },
|
|
1165
|
-
{ name: "At Risk", value: "at_risk" },
|
|
1166
|
-
{ name: "Completed", value: "completed" },
|
|
1167
|
-
],
|
|
1168
|
-
default: "not_started",
|
|
1169
|
-
});
|
|
1170
|
-
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
1171
|
-
method: "POST",
|
|
1172
|
-
headers: {
|
|
1173
|
-
"Content-Type": "application/json",
|
|
1174
|
-
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
1175
|
-
},
|
|
1176
|
-
body: JSON.stringify({
|
|
1177
|
-
type: "goal",
|
|
1178
|
-
title,
|
|
1179
|
-
description: description || "",
|
|
1180
|
-
status,
|
|
1181
|
-
progress: 0,
|
|
1182
|
-
}),
|
|
1183
|
-
});
|
|
1184
|
-
if (!res.ok) {
|
|
1185
|
-
console.error(`\n Error: API returned ${res.status}\n`);
|
|
1186
|
-
await pressEnterToContinue();
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
console.log(`\n Goal added successfully!\n`);
|
|
1190
|
-
await pressEnterToContinue();
|
|
1191
|
-
}
|
|
1192
|
-
catch (error) {
|
|
1193
|
-
console.error("\n Error adding goal:", error);
|
|
1194
|
-
await pressEnterToContinue();
|
|
1195
110
|
}
|
|
1196
111
|
}
|
|
1197
112
|
// ============================================
|
|
1198
|
-
// SETTINGS MENU
|
|
113
|
+
// SETTINGS MENU (kept in main file - small)
|
|
1199
114
|
// ============================================
|
|
1200
115
|
async function settingsMenu() {
|
|
1201
|
-
const config =
|
|
116
|
+
const config = getConfig();
|
|
117
|
+
if (!config.apiUrl) {
|
|
118
|
+
console.error("Error: API URL not configured. Run: husky config set api-url <url>");
|
|
119
|
+
await pressEnterToContinue();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
1202
122
|
const menuItems = [
|
|
1203
123
|
{ name: "Show all settings", value: "show" },
|
|
1204
124
|
{ name: "Update a setting", value: "update" },
|
|
@@ -1258,7 +178,7 @@ async function updateSetting(config) {
|
|
|
1258
178
|
});
|
|
1259
179
|
const value = await input({
|
|
1260
180
|
message: "New value:",
|
|
1261
|
-
validate: (
|
|
181
|
+
validate: (v) => (v.length > 0 ? true : "Value is required"),
|
|
1262
182
|
});
|
|
1263
183
|
// Try to parse as JSON
|
|
1264
184
|
let parsedValue = value;
|
|
@@ -1290,7 +210,7 @@ async function updateSetting(config) {
|
|
|
1290
210
|
}
|
|
1291
211
|
}
|
|
1292
212
|
// ============================================
|
|
1293
|
-
// CLI CONFIG MENU
|
|
213
|
+
// CLI CONFIG MENU (kept in main file - small)
|
|
1294
214
|
// ============================================
|
|
1295
215
|
async function cliConfigMenu() {
|
|
1296
216
|
const menuItems = [
|
|
@@ -1331,7 +251,6 @@ async function showCliConfig() {
|
|
|
1331
251
|
await pressEnterToContinue();
|
|
1332
252
|
}
|
|
1333
253
|
async function setApiUrl() {
|
|
1334
|
-
const { setConfig } = await import("./config.js");
|
|
1335
254
|
const url = await input({
|
|
1336
255
|
message: "API URL (e.g., https://your-dashboard.run.app):",
|
|
1337
256
|
validate: (value) => {
|
|
@@ -1343,23 +262,22 @@ async function setApiUrl() {
|
|
|
1343
262
|
},
|
|
1344
263
|
});
|
|
1345
264
|
setConfig("apiUrl", url);
|
|
1346
|
-
console.log("\n
|
|
265
|
+
console.log("\n API URL saved!\n");
|
|
1347
266
|
await pressEnterToContinue();
|
|
1348
267
|
}
|
|
1349
268
|
async function setApiKey() {
|
|
1350
|
-
const { setConfig } = await import("./config.js");
|
|
1351
269
|
const key = await input({
|
|
1352
270
|
message: "API Key:",
|
|
1353
271
|
validate: (value) => (value.length > 0 ? true : "API Key is required"),
|
|
1354
272
|
});
|
|
1355
273
|
setConfig("apiKey", key);
|
|
1356
|
-
console.log("\n
|
|
274
|
+
console.log("\n API Key saved!\n");
|
|
1357
275
|
await pressEnterToContinue();
|
|
1358
276
|
}
|
|
1359
277
|
async function testConnection() {
|
|
1360
278
|
const config = getConfig();
|
|
1361
279
|
if (!config.apiUrl) {
|
|
1362
|
-
console.log("\n
|
|
280
|
+
console.log("\n API URL not configured. Set it first.\n");
|
|
1363
281
|
await pressEnterToContinue();
|
|
1364
282
|
return;
|
|
1365
283
|
}
|
|
@@ -1369,29 +287,21 @@ async function testConnection() {
|
|
|
1369
287
|
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
1370
288
|
});
|
|
1371
289
|
if (res.ok) {
|
|
1372
|
-
console.log("
|
|
290
|
+
console.log(" Connection successful!");
|
|
1373
291
|
console.log(` Status: ${res.status}`);
|
|
1374
292
|
}
|
|
1375
293
|
else if (res.status === 401) {
|
|
1376
|
-
console.log("
|
|
294
|
+
console.log(" Connection works but authentication failed.");
|
|
1377
295
|
console.log(" Check your API Key.");
|
|
1378
296
|
}
|
|
1379
297
|
else {
|
|
1380
|
-
console.log(`
|
|
298
|
+
console.log(` Connection failed with status: ${res.status}`);
|
|
1381
299
|
}
|
|
1382
300
|
}
|
|
1383
301
|
catch (error) {
|
|
1384
|
-
console.log("
|
|
302
|
+
console.log(" Connection failed!");
|
|
1385
303
|
console.log(` Error: ${error.message}`);
|
|
1386
304
|
}
|
|
1387
305
|
console.log("");
|
|
1388
306
|
await pressEnterToContinue();
|
|
1389
307
|
}
|
|
1390
|
-
// ============================================
|
|
1391
|
-
// UTILITY FUNCTIONS
|
|
1392
|
-
// ============================================
|
|
1393
|
-
async function pressEnterToContinue() {
|
|
1394
|
-
await input({
|
|
1395
|
-
message: "Press Enter to continue...",
|
|
1396
|
-
});
|
|
1397
|
-
}
|