@simonfestl/husky-cli 0.3.0 → 0.5.1
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 +228 -58
- package/dist/commands/changelog.d.ts +2 -0
- package/dist/commands/changelog.js +401 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +400 -0
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +101 -1
- package/dist/commands/department.d.ts +2 -0
- package/dist/commands/department.js +240 -0
- package/dist/commands/explain.d.ts +2 -0
- package/dist/commands/explain.js +411 -0
- package/dist/commands/idea.d.ts +2 -0
- package/dist/commands/idea.js +340 -0
- package/dist/commands/interactive.d.ts +1 -0
- package/dist/commands/interactive.js +1397 -0
- package/dist/commands/jules.d.ts +2 -0
- package/dist/commands/jules.js +593 -0
- package/dist/commands/process.d.ts +2 -0
- package/dist/commands/process.js +289 -0
- package/dist/commands/project.d.ts +2 -0
- package/dist/commands/project.js +473 -0
- package/dist/commands/roadmap.js +318 -0
- package/dist/commands/settings.d.ts +2 -0
- package/dist/commands/settings.js +153 -0
- package/dist/commands/strategy.d.ts +2 -0
- package/dist/commands/strategy.js +706 -0
- package/dist/commands/task.js +244 -1
- package/dist/commands/vm-config.d.ts +2 -0
- package/dist/commands/vm-config.js +318 -0
- package/dist/commands/vm.d.ts +2 -0
- package/dist/commands/vm.js +621 -0
- package/dist/commands/workflow.d.ts +2 -0
- package/dist/commands/workflow.js +545 -0
- package/dist/index.js +35 -2
- package/package.json +8 -2
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getConfig } from "./config.js";
|
|
3
|
+
export const strategyCommand = new Command("strategy")
|
|
4
|
+
.description("Manage business strategy");
|
|
5
|
+
// Helper: Ensure API is configured
|
|
6
|
+
function ensureConfig() {
|
|
7
|
+
const config = getConfig();
|
|
8
|
+
if (!config.apiUrl) {
|
|
9
|
+
console.error("Error: API URL not configured. Run: husky config set api-url <url>");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
return config;
|
|
13
|
+
}
|
|
14
|
+
// husky strategy show
|
|
15
|
+
strategyCommand
|
|
16
|
+
.command("show")
|
|
17
|
+
.description("Show current business strategy")
|
|
18
|
+
.option("--json", "Output as JSON")
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
const config = ensureConfig();
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
23
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
throw new Error(`API error: ${res.status}`);
|
|
27
|
+
}
|
|
28
|
+
const strategy = await res.json();
|
|
29
|
+
if (options.json) {
|
|
30
|
+
console.log(JSON.stringify(strategy, null, 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
printStrategy(strategy);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error("Error fetching business strategy:", error);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
// husky strategy set-vision
|
|
42
|
+
strategyCommand
|
|
43
|
+
.command("set-vision")
|
|
44
|
+
.description("Set/update the company vision")
|
|
45
|
+
.requiredOption("-v, --vision <text>", "Vision text")
|
|
46
|
+
.option("--json", "Output as JSON")
|
|
47
|
+
.action(async (options) => {
|
|
48
|
+
const config = ensureConfig();
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
51
|
+
method: "PATCH",
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify({ vision: options.vision }),
|
|
57
|
+
});
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
60
|
+
throw new Error(errorBody.error || `API error: ${res.status}`);
|
|
61
|
+
}
|
|
62
|
+
const strategy = await res.json();
|
|
63
|
+
if (options.json) {
|
|
64
|
+
console.log(JSON.stringify(strategy, null, 2));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log("Vision updated successfully");
|
|
68
|
+
console.log(` Vision: ${strategy.vision}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error("Error updating vision:", error);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
// husky strategy set-mission
|
|
77
|
+
strategyCommand
|
|
78
|
+
.command("set-mission")
|
|
79
|
+
.description("Set/update the company mission")
|
|
80
|
+
.requiredOption("-m, --mission <text>", "Mission text")
|
|
81
|
+
.option("--json", "Output as JSON")
|
|
82
|
+
.action(async (options) => {
|
|
83
|
+
const config = ensureConfig();
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
86
|
+
method: "PATCH",
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({ mission: options.mission }),
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
95
|
+
throw new Error(errorBody.error || `API error: ${res.status}`);
|
|
96
|
+
}
|
|
97
|
+
const strategy = await res.json();
|
|
98
|
+
if (options.json) {
|
|
99
|
+
console.log(JSON.stringify(strategy, null, 2));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log("Mission updated successfully");
|
|
103
|
+
console.log(` Mission: ${strategy.mission}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error("Error updating mission:", error);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// husky strategy add-value
|
|
112
|
+
strategyCommand
|
|
113
|
+
.command("add-value")
|
|
114
|
+
.description("Add a company value")
|
|
115
|
+
.requiredOption("-n, --name <name>", "Value name")
|
|
116
|
+
.option("-d, --description <desc>", "Value description")
|
|
117
|
+
.option("--json", "Output as JSON")
|
|
118
|
+
.action(async (options) => {
|
|
119
|
+
const config = ensureConfig();
|
|
120
|
+
try {
|
|
121
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: {
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
type: "value",
|
|
129
|
+
name: options.name,
|
|
130
|
+
description: options.description || "",
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
if (!res.ok) {
|
|
134
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
135
|
+
throw new Error(errorBody.error || `API error: ${res.status}`);
|
|
136
|
+
}
|
|
137
|
+
const strategy = await res.json();
|
|
138
|
+
const newValue = strategy.values[strategy.values.length - 1];
|
|
139
|
+
if (options.json) {
|
|
140
|
+
console.log(JSON.stringify(newValue, null, 2));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log("Value added successfully");
|
|
144
|
+
console.log(` ID: ${newValue.id}`);
|
|
145
|
+
console.log(` Name: ${newValue.name}`);
|
|
146
|
+
if (newValue.description) {
|
|
147
|
+
console.log(` Description: ${newValue.description}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error("Error adding value:", error);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// husky strategy update-value <id>
|
|
157
|
+
strategyCommand
|
|
158
|
+
.command("update-value <id>")
|
|
159
|
+
.description("Update a company value")
|
|
160
|
+
.option("-n, --name <name>", "New name")
|
|
161
|
+
.option("-d, --description <desc>", "New description")
|
|
162
|
+
.option("--json", "Output as JSON")
|
|
163
|
+
.action(async (id, options) => {
|
|
164
|
+
const config = ensureConfig();
|
|
165
|
+
// Build update payload
|
|
166
|
+
const updateData = {};
|
|
167
|
+
if (options.name) {
|
|
168
|
+
updateData.name = options.name;
|
|
169
|
+
}
|
|
170
|
+
if (options.description !== undefined) {
|
|
171
|
+
updateData.description = options.description;
|
|
172
|
+
}
|
|
173
|
+
if (Object.keys(updateData).length === 0) {
|
|
174
|
+
console.error("Error: No update options provided. Use -n or -d");
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy/values/${id}`, {
|
|
179
|
+
method: "PATCH",
|
|
180
|
+
headers: {
|
|
181
|
+
"Content-Type": "application/json",
|
|
182
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
183
|
+
},
|
|
184
|
+
body: JSON.stringify(updateData),
|
|
185
|
+
});
|
|
186
|
+
if (!res.ok) {
|
|
187
|
+
if (res.status === 404) {
|
|
188
|
+
console.error(`Error: Value ${id} not found`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
192
|
+
console.error(`Error: ${errorBody.error || `API returned ${res.status}`}`);
|
|
193
|
+
}
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
const strategy = await res.json();
|
|
197
|
+
const updatedValue = strategy.values.find((v) => v.id === id);
|
|
198
|
+
if (options.json) {
|
|
199
|
+
console.log(JSON.stringify(updatedValue, null, 2));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log("Value updated successfully");
|
|
203
|
+
if (updatedValue) {
|
|
204
|
+
console.log(` Name: ${updatedValue.name}`);
|
|
205
|
+
if (updatedValue.description) {
|
|
206
|
+
console.log(` Description: ${updatedValue.description}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.error("Error updating value:", error);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
// husky strategy delete-value <id>
|
|
217
|
+
strategyCommand
|
|
218
|
+
.command("delete-value <id>")
|
|
219
|
+
.description("Delete a company value")
|
|
220
|
+
.option("--force", "Skip confirmation")
|
|
221
|
+
.action(async (id, options) => {
|
|
222
|
+
const config = ensureConfig();
|
|
223
|
+
if (!options.force) {
|
|
224
|
+
console.log("Warning: This will permanently delete the value.");
|
|
225
|
+
console.log("Use --force to confirm deletion.");
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy/values/${id}`, {
|
|
230
|
+
method: "DELETE",
|
|
231
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
232
|
+
});
|
|
233
|
+
if (!res.ok) {
|
|
234
|
+
if (res.status === 404) {
|
|
235
|
+
console.error(`Error: Value ${id} not found`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
console.error(`Error: API returned ${res.status}`);
|
|
239
|
+
}
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
console.log("Value deleted successfully");
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
console.error("Error deleting value:", error);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
// husky strategy add-goal
|
|
250
|
+
strategyCommand
|
|
251
|
+
.command("add-goal")
|
|
252
|
+
.description("Add a strategic goal")
|
|
253
|
+
.requiredOption("-t, --title <title>", "Goal title")
|
|
254
|
+
.option("-d, --description <desc>", "Goal description")
|
|
255
|
+
.option("--status <status>", "Status (not_started, in_progress, at_risk, completed)", "not_started")
|
|
256
|
+
.option("--progress <n>", "Progress percentage (0-100)", "0")
|
|
257
|
+
.option("--due <date>", "Due date (ISO format)")
|
|
258
|
+
.option("--json", "Output as JSON")
|
|
259
|
+
.action(async (options) => {
|
|
260
|
+
const config = ensureConfig();
|
|
261
|
+
// Validate status
|
|
262
|
+
const validStatuses = ["not_started", "in_progress", "at_risk", "completed"];
|
|
263
|
+
if (!validStatuses.includes(options.status)) {
|
|
264
|
+
console.error(`Error: Invalid status "${options.status}". Must be one of: ${validStatuses.join(", ")}`);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
// Validate progress
|
|
268
|
+
const progress = parseInt(options.progress, 10);
|
|
269
|
+
if (isNaN(progress) || progress < 0 || progress > 100) {
|
|
270
|
+
console.error("Error: Progress must be a number between 0 and 100");
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const body = {
|
|
275
|
+
type: "goal",
|
|
276
|
+
title: options.title,
|
|
277
|
+
description: options.description || "",
|
|
278
|
+
status: options.status,
|
|
279
|
+
progress,
|
|
280
|
+
};
|
|
281
|
+
if (options.due) {
|
|
282
|
+
body.dueDate = options.due;
|
|
283
|
+
}
|
|
284
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
"Content-Type": "application/json",
|
|
288
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
289
|
+
},
|
|
290
|
+
body: JSON.stringify(body),
|
|
291
|
+
});
|
|
292
|
+
if (!res.ok) {
|
|
293
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
294
|
+
throw new Error(errorBody.error || `API error: ${res.status}`);
|
|
295
|
+
}
|
|
296
|
+
const strategy = await res.json();
|
|
297
|
+
const newGoal = strategy.goals[strategy.goals.length - 1];
|
|
298
|
+
if (options.json) {
|
|
299
|
+
console.log(JSON.stringify(newGoal, null, 2));
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
console.log("Goal added successfully");
|
|
303
|
+
console.log(` ID: ${newGoal.id}`);
|
|
304
|
+
console.log(` Title: ${newGoal.title}`);
|
|
305
|
+
console.log(` Status: ${newGoal.status}`);
|
|
306
|
+
console.log(` Progress: ${newGoal.progress}%`);
|
|
307
|
+
if (newGoal.dueDate) {
|
|
308
|
+
console.log(` Due: ${new Date(newGoal.dueDate).toLocaleDateString()}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
console.error("Error adding goal:", error);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
// husky strategy update-goal <id>
|
|
318
|
+
strategyCommand
|
|
319
|
+
.command("update-goal <id>")
|
|
320
|
+
.description("Update a strategic goal")
|
|
321
|
+
.option("-t, --title <title>", "New title")
|
|
322
|
+
.option("-d, --description <desc>", "New description")
|
|
323
|
+
.option("--status <status>", "New status (not_started, in_progress, at_risk, completed)")
|
|
324
|
+
.option("--progress <n>", "New progress percentage (0-100)")
|
|
325
|
+
.option("--due <date>", "New due date (ISO format)")
|
|
326
|
+
.option("--json", "Output as JSON")
|
|
327
|
+
.action(async (id, options) => {
|
|
328
|
+
const config = ensureConfig();
|
|
329
|
+
// Build update payload
|
|
330
|
+
const updateData = {};
|
|
331
|
+
if (options.title) {
|
|
332
|
+
updateData.title = options.title;
|
|
333
|
+
}
|
|
334
|
+
if (options.description !== undefined) {
|
|
335
|
+
updateData.description = options.description;
|
|
336
|
+
}
|
|
337
|
+
if (options.status) {
|
|
338
|
+
const validStatuses = ["not_started", "in_progress", "at_risk", "completed"];
|
|
339
|
+
if (!validStatuses.includes(options.status)) {
|
|
340
|
+
console.error(`Error: Invalid status "${options.status}". Must be one of: ${validStatuses.join(", ")}`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
updateData.status = options.status;
|
|
344
|
+
}
|
|
345
|
+
if (options.progress !== undefined) {
|
|
346
|
+
const progress = parseInt(options.progress, 10);
|
|
347
|
+
if (isNaN(progress) || progress < 0 || progress > 100) {
|
|
348
|
+
console.error("Error: Progress must be a number between 0 and 100");
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
updateData.progress = progress;
|
|
352
|
+
}
|
|
353
|
+
if (options.due) {
|
|
354
|
+
updateData.dueDate = options.due;
|
|
355
|
+
}
|
|
356
|
+
if (Object.keys(updateData).length === 0) {
|
|
357
|
+
console.error("Error: No update options provided. Use -t, -d, --status, --progress, or --due");
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy/goals/${id}`, {
|
|
362
|
+
method: "PATCH",
|
|
363
|
+
headers: {
|
|
364
|
+
"Content-Type": "application/json",
|
|
365
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
366
|
+
},
|
|
367
|
+
body: JSON.stringify(updateData),
|
|
368
|
+
});
|
|
369
|
+
if (!res.ok) {
|
|
370
|
+
if (res.status === 404) {
|
|
371
|
+
console.error(`Error: Goal ${id} not found`);
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
375
|
+
console.error(`Error: ${errorBody.error || `API returned ${res.status}`}`);
|
|
376
|
+
}
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
const strategy = await res.json();
|
|
380
|
+
const updatedGoal = strategy.goals.find((g) => g.id === id);
|
|
381
|
+
if (options.json) {
|
|
382
|
+
console.log(JSON.stringify(updatedGoal, null, 2));
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
console.log("Goal updated successfully");
|
|
386
|
+
if (updatedGoal) {
|
|
387
|
+
console.log(` Title: ${updatedGoal.title}`);
|
|
388
|
+
console.log(` Status: ${updatedGoal.status}`);
|
|
389
|
+
console.log(` Progress: ${updatedGoal.progress}%`);
|
|
390
|
+
if (updatedGoal.dueDate) {
|
|
391
|
+
console.log(` Due: ${new Date(updatedGoal.dueDate).toLocaleDateString()}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
console.error("Error updating goal:", error);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
// husky strategy delete-goal <id>
|
|
402
|
+
strategyCommand
|
|
403
|
+
.command("delete-goal <id>")
|
|
404
|
+
.description("Delete a strategic goal")
|
|
405
|
+
.option("--force", "Skip confirmation")
|
|
406
|
+
.action(async (id, options) => {
|
|
407
|
+
const config = ensureConfig();
|
|
408
|
+
if (!options.force) {
|
|
409
|
+
console.log("Warning: This will permanently delete the goal.");
|
|
410
|
+
console.log("Use --force to confirm deletion.");
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy/goals/${id}`, {
|
|
415
|
+
method: "DELETE",
|
|
416
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
417
|
+
});
|
|
418
|
+
if (!res.ok) {
|
|
419
|
+
if (res.status === 404) {
|
|
420
|
+
console.error(`Error: Goal ${id} not found`);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
console.error(`Error: API returned ${res.status}`);
|
|
424
|
+
}
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
console.log("Goal deleted successfully");
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
console.error("Error deleting goal:", error);
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
// husky strategy add-persona
|
|
435
|
+
strategyCommand
|
|
436
|
+
.command("add-persona")
|
|
437
|
+
.description("Add a target audience persona")
|
|
438
|
+
.requiredOption("-n, --name <name>", "Persona name")
|
|
439
|
+
.option("-d, --description <desc>", "Persona description")
|
|
440
|
+
.option("--characteristics <list>", "Comma-separated characteristics")
|
|
441
|
+
.option("--json", "Output as JSON")
|
|
442
|
+
.action(async (options) => {
|
|
443
|
+
const config = ensureConfig();
|
|
444
|
+
// Parse characteristics
|
|
445
|
+
const characteristics = options.characteristics
|
|
446
|
+
? options.characteristics.split(",").map((c) => c.trim()).filter((c) => c)
|
|
447
|
+
: [];
|
|
448
|
+
try {
|
|
449
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy`, {
|
|
450
|
+
method: "POST",
|
|
451
|
+
headers: {
|
|
452
|
+
"Content-Type": "application/json",
|
|
453
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
454
|
+
},
|
|
455
|
+
body: JSON.stringify({
|
|
456
|
+
type: "persona",
|
|
457
|
+
name: options.name,
|
|
458
|
+
description: options.description || "",
|
|
459
|
+
characteristics,
|
|
460
|
+
}),
|
|
461
|
+
});
|
|
462
|
+
if (!res.ok) {
|
|
463
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
464
|
+
throw new Error(errorBody.error || `API error: ${res.status}`);
|
|
465
|
+
}
|
|
466
|
+
const strategy = await res.json();
|
|
467
|
+
const newPersona = strategy.targetAudience[strategy.targetAudience.length - 1];
|
|
468
|
+
if (options.json) {
|
|
469
|
+
console.log(JSON.stringify(newPersona, null, 2));
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
console.log("Persona added successfully");
|
|
473
|
+
console.log(` ID: ${newPersona.id}`);
|
|
474
|
+
console.log(` Name: ${newPersona.name}`);
|
|
475
|
+
if (newPersona.description) {
|
|
476
|
+
console.log(` Description: ${newPersona.description}`);
|
|
477
|
+
}
|
|
478
|
+
if (newPersona.characteristics.length > 0) {
|
|
479
|
+
console.log(` Characteristics:`);
|
|
480
|
+
for (const c of newPersona.characteristics) {
|
|
481
|
+
console.log(` - ${c}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
console.error("Error adding persona:", error);
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
// husky strategy update-persona <id>
|
|
492
|
+
strategyCommand
|
|
493
|
+
.command("update-persona <id>")
|
|
494
|
+
.description("Update a target audience persona")
|
|
495
|
+
.option("-n, --name <name>", "New name")
|
|
496
|
+
.option("-d, --description <desc>", "New description")
|
|
497
|
+
.option("--characteristics <list>", "Comma-separated characteristics")
|
|
498
|
+
.option("--json", "Output as JSON")
|
|
499
|
+
.action(async (id, options) => {
|
|
500
|
+
const config = ensureConfig();
|
|
501
|
+
// Build update payload
|
|
502
|
+
const updateData = {};
|
|
503
|
+
if (options.name) {
|
|
504
|
+
updateData.name = options.name;
|
|
505
|
+
}
|
|
506
|
+
if (options.description !== undefined) {
|
|
507
|
+
updateData.description = options.description;
|
|
508
|
+
}
|
|
509
|
+
if (options.characteristics !== undefined) {
|
|
510
|
+
updateData.characteristics = options.characteristics
|
|
511
|
+
.split(",")
|
|
512
|
+
.map((c) => c.trim())
|
|
513
|
+
.filter((c) => c);
|
|
514
|
+
}
|
|
515
|
+
if (Object.keys(updateData).length === 0) {
|
|
516
|
+
console.error("Error: No update options provided. Use -n, -d, or --characteristics");
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy/personas/${id}`, {
|
|
521
|
+
method: "PATCH",
|
|
522
|
+
headers: {
|
|
523
|
+
"Content-Type": "application/json",
|
|
524
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
525
|
+
},
|
|
526
|
+
body: JSON.stringify(updateData),
|
|
527
|
+
});
|
|
528
|
+
if (!res.ok) {
|
|
529
|
+
if (res.status === 404) {
|
|
530
|
+
console.error(`Error: Persona ${id} not found`);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
534
|
+
console.error(`Error: ${errorBody.error || `API returned ${res.status}`}`);
|
|
535
|
+
}
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
const strategy = await res.json();
|
|
539
|
+
const updatedPersona = strategy.targetAudience.find((p) => p.id === id);
|
|
540
|
+
if (options.json) {
|
|
541
|
+
console.log(JSON.stringify(updatedPersona, null, 2));
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
console.log("Persona updated successfully");
|
|
545
|
+
if (updatedPersona) {
|
|
546
|
+
console.log(` Name: ${updatedPersona.name}`);
|
|
547
|
+
if (updatedPersona.description) {
|
|
548
|
+
console.log(` Description: ${updatedPersona.description}`);
|
|
549
|
+
}
|
|
550
|
+
if (updatedPersona.characteristics.length > 0) {
|
|
551
|
+
console.log(` Characteristics:`);
|
|
552
|
+
for (const c of updatedPersona.characteristics) {
|
|
553
|
+
console.log(` - ${c}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
console.error("Error updating persona:", error);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
// husky strategy delete-persona <id>
|
|
565
|
+
strategyCommand
|
|
566
|
+
.command("delete-persona <id>")
|
|
567
|
+
.description("Delete a target audience persona")
|
|
568
|
+
.option("--force", "Skip confirmation")
|
|
569
|
+
.action(async (id, options) => {
|
|
570
|
+
const config = ensureConfig();
|
|
571
|
+
if (!options.force) {
|
|
572
|
+
console.log("Warning: This will permanently delete the persona.");
|
|
573
|
+
console.log("Use --force to confirm deletion.");
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
576
|
+
try {
|
|
577
|
+
const res = await fetch(`${config.apiUrl}/api/business-strategy/personas/${id}`, {
|
|
578
|
+
method: "DELETE",
|
|
579
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
580
|
+
});
|
|
581
|
+
if (!res.ok) {
|
|
582
|
+
if (res.status === 404) {
|
|
583
|
+
console.error(`Error: Persona ${id} not found`);
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
console.error(`Error: API returned ${res.status}`);
|
|
587
|
+
}
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
console.log("Persona deleted successfully");
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
console.error("Error deleting persona:", error);
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
// Print functions
|
|
598
|
+
function printStrategy(strategy) {
|
|
599
|
+
console.log("\n BUSINESS STRATEGY");
|
|
600
|
+
console.log(" " + "=".repeat(60));
|
|
601
|
+
// Vision & Mission
|
|
602
|
+
console.log("\n VISION");
|
|
603
|
+
console.log(" " + "-".repeat(40));
|
|
604
|
+
if (strategy.vision) {
|
|
605
|
+
console.log(` ${strategy.vision}`);
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
console.log(" (not set)");
|
|
609
|
+
}
|
|
610
|
+
console.log("\n MISSION");
|
|
611
|
+
console.log(" " + "-".repeat(40));
|
|
612
|
+
if (strategy.mission) {
|
|
613
|
+
console.log(` ${strategy.mission}`);
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
console.log(" (not set)");
|
|
617
|
+
}
|
|
618
|
+
// Values
|
|
619
|
+
console.log("\n VALUES (" + strategy.values.length + ")");
|
|
620
|
+
console.log(" " + "-".repeat(40));
|
|
621
|
+
if (strategy.values.length === 0) {
|
|
622
|
+
console.log(" (none)");
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
for (const value of strategy.values) {
|
|
626
|
+
console.log(` - ${value.name}`);
|
|
627
|
+
if (value.description) {
|
|
628
|
+
console.log(` ${value.description}`);
|
|
629
|
+
}
|
|
630
|
+
console.log(` ID: ${value.id}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// Goals
|
|
634
|
+
console.log("\n STRATEGIC GOALS (" + strategy.goals.length + ")");
|
|
635
|
+
console.log(" " + "-".repeat(40));
|
|
636
|
+
if (strategy.goals.length === 0) {
|
|
637
|
+
console.log(" (none)");
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
for (const goal of strategy.goals) {
|
|
641
|
+
const statusIcon = getGoalStatusIcon(goal.status);
|
|
642
|
+
const progressBar = getProgressBar(goal.progress);
|
|
643
|
+
console.log(` ${statusIcon} ${goal.title}`);
|
|
644
|
+
console.log(` Progress: ${progressBar} ${goal.progress}%`);
|
|
645
|
+
console.log(` Status: ${formatGoalStatus(goal.status)}`);
|
|
646
|
+
if (goal.dueDate) {
|
|
647
|
+
console.log(` Due: ${new Date(goal.dueDate).toLocaleDateString()}`);
|
|
648
|
+
}
|
|
649
|
+
console.log(` ID: ${goal.id}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
// Target Audience Personas
|
|
653
|
+
console.log("\n TARGET AUDIENCE PERSONAS (" + strategy.targetAudience.length + ")");
|
|
654
|
+
console.log(" " + "-".repeat(40));
|
|
655
|
+
if (strategy.targetAudience.length === 0) {
|
|
656
|
+
console.log(" (none)");
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
for (const persona of strategy.targetAudience) {
|
|
660
|
+
console.log(` - ${persona.name}`);
|
|
661
|
+
if (persona.description) {
|
|
662
|
+
console.log(` ${persona.description}`);
|
|
663
|
+
}
|
|
664
|
+
if (persona.characteristics.length > 0) {
|
|
665
|
+
console.log(` Characteristics: ${persona.characteristics.join(", ")}`);
|
|
666
|
+
}
|
|
667
|
+
console.log(` ID: ${persona.id}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
console.log("\n " + "-".repeat(40));
|
|
671
|
+
console.log(` Last updated: ${new Date(strategy.updatedAt).toLocaleString()}`);
|
|
672
|
+
console.log("");
|
|
673
|
+
}
|
|
674
|
+
function getGoalStatusIcon(status) {
|
|
675
|
+
switch (status) {
|
|
676
|
+
case "not_started":
|
|
677
|
+
return "[ ]";
|
|
678
|
+
case "in_progress":
|
|
679
|
+
return "[>]";
|
|
680
|
+
case "at_risk":
|
|
681
|
+
return "[!]";
|
|
682
|
+
case "completed":
|
|
683
|
+
return "[x]";
|
|
684
|
+
default:
|
|
685
|
+
return "[ ]";
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
function formatGoalStatus(status) {
|
|
689
|
+
switch (status) {
|
|
690
|
+
case "not_started":
|
|
691
|
+
return "Not Started";
|
|
692
|
+
case "in_progress":
|
|
693
|
+
return "In Progress";
|
|
694
|
+
case "at_risk":
|
|
695
|
+
return "At Risk";
|
|
696
|
+
case "completed":
|
|
697
|
+
return "Completed";
|
|
698
|
+
default:
|
|
699
|
+
return status;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function getProgressBar(progress) {
|
|
703
|
+
const filled = Math.round(progress / 10);
|
|
704
|
+
const empty = 10 - filled;
|
|
705
|
+
return "[" + "#".repeat(filled) + "-".repeat(empty) + "]";
|
|
706
|
+
}
|