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