@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.
@@ -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
+ }