@cadiraca/crowntrack-cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +207 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,12 +79,45 @@ async function deleteProject(id, confirm) {
|
|
|
79
79
|
async function getStats() {
|
|
80
80
|
return request("/api/stats");
|
|
81
81
|
}
|
|
82
|
+
async function listActions(filters) {
|
|
83
|
+
const params = new URLSearchParams();
|
|
84
|
+
if (filters?.projectId) params.set("projectId", filters.projectId);
|
|
85
|
+
if (filters?.search) params.set("search", filters.search);
|
|
86
|
+
if (filters?.done) params.set("done", filters.done);
|
|
87
|
+
const qs = params.toString();
|
|
88
|
+
return request(`/api/actions${qs ? `?${qs}` : ""}`);
|
|
89
|
+
}
|
|
90
|
+
async function search(query) {
|
|
91
|
+
return request(`/api/search?q=${encodeURIComponent(query)}`);
|
|
92
|
+
}
|
|
93
|
+
async function fixDuplicateIds() {
|
|
94
|
+
return request("/api/fix-duplicate-ids", { method: "POST" });
|
|
95
|
+
}
|
|
96
|
+
async function fixDuplicateProjectIds() {
|
|
97
|
+
return request("/api/fix-duplicate-project-ids", { method: "POST" });
|
|
98
|
+
}
|
|
99
|
+
async function normalize() {
|
|
100
|
+
return request("/api/normalize", { method: "POST" });
|
|
101
|
+
}
|
|
102
|
+
async function exportData(format) {
|
|
103
|
+
const url = `${BASE_URL}/api/export?format=${format}`;
|
|
104
|
+
const res = await fetch(url);
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
const text = await res.text();
|
|
107
|
+
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
108
|
+
}
|
|
109
|
+
if (format === "csv") {
|
|
110
|
+
return res.text();
|
|
111
|
+
}
|
|
112
|
+
return res.json();
|
|
113
|
+
}
|
|
82
114
|
|
|
83
115
|
// src/index.ts
|
|
116
|
+
import { writeFileSync } from "fs";
|
|
84
117
|
var program = new Command();
|
|
85
118
|
var amber = chalk.hex("#f59e0b");
|
|
86
119
|
var dim = chalk.gray;
|
|
87
|
-
program.name("ct").description(amber("\u{1F451} CrownTrack CLI") + " \u2014 Rule your projects from the terminal").version("0.
|
|
120
|
+
program.name("ct").description(amber("\u{1F451} CrownTrack CLI") + " \u2014 Rule your projects from the terminal").version("0.2.0");
|
|
88
121
|
program.command("projects").description("List all projects").option("-s, --status <status>", "Filter by status").option("-c, --category <category>", "Filter by category").option("-p, --priority <priority>", "Filter by priority").option("--search <query>", "Search by name").option("--tag <tag>", "Filter by tag").action(async (opts) => {
|
|
89
122
|
try {
|
|
90
123
|
const projects = await listProjects({
|
|
@@ -356,6 +389,179 @@ ${chalk.green("\u2713")} All active projects updated recently`);
|
|
|
356
389
|
handleError(e);
|
|
357
390
|
}
|
|
358
391
|
});
|
|
392
|
+
program.command("actions").description("List actions across all projects").option("-a, --all", "Include completed actions").option("-p, --project <id>", "Filter by project ID or name").option("--search <query>", "Search action titles").action(async (opts) => {
|
|
393
|
+
try {
|
|
394
|
+
let projectId;
|
|
395
|
+
if (opts.project) {
|
|
396
|
+
const p = await resolveProject(opts.project);
|
|
397
|
+
projectId = p.id;
|
|
398
|
+
}
|
|
399
|
+
const actions = await listActions({
|
|
400
|
+
projectId,
|
|
401
|
+
search: opts.search,
|
|
402
|
+
done: opts.all ? "all" : "false"
|
|
403
|
+
});
|
|
404
|
+
if (actions.length === 0) {
|
|
405
|
+
console.log(dim("No actions found."));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const table = new Table({
|
|
409
|
+
head: [
|
|
410
|
+
dim("Action ID"),
|
|
411
|
+
amber("Project"),
|
|
412
|
+
"Title",
|
|
413
|
+
"Priority",
|
|
414
|
+
"Status",
|
|
415
|
+
"Age"
|
|
416
|
+
],
|
|
417
|
+
style: { head: [], border: ["gray"] }
|
|
418
|
+
});
|
|
419
|
+
for (const a of actions) {
|
|
420
|
+
table.push([
|
|
421
|
+
dim(a.id.slice(0, 8)),
|
|
422
|
+
a.project.name,
|
|
423
|
+
a.done ? dim(a.title) : a.title,
|
|
424
|
+
priorityColor(a.priority),
|
|
425
|
+
a.done ? chalk.green("done") : chalk.yellow("open"),
|
|
426
|
+
timeAgo(a.createdAt)
|
|
427
|
+
]);
|
|
428
|
+
}
|
|
429
|
+
console.log(table.toString());
|
|
430
|
+
const open = actions.filter((a) => !a.done).length;
|
|
431
|
+
const done = actions.filter((a) => a.done).length;
|
|
432
|
+
console.log(dim(`
|
|
433
|
+
${actions.length} action(s) \u2014 ${open} open, ${done} done`));
|
|
434
|
+
} catch (e) {
|
|
435
|
+
handleError(e);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
program.command("search <query>").description("Search across all projects \u2014 actions, notes, decisions").action(async (query) => {
|
|
439
|
+
try {
|
|
440
|
+
const results = await search(query);
|
|
441
|
+
if (results.totalMatches === 0) {
|
|
442
|
+
console.log(dim(`No matches for "${query}"`));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
console.log(amber(`
|
|
446
|
+
\u{1F50D} Search results for "${query}"`) + dim(` (${results.totalMatches} match${results.totalMatches === 1 ? "" : "es"})
|
|
447
|
+
`));
|
|
448
|
+
if (results.results.actions.length > 0) {
|
|
449
|
+
console.log(amber("\u2705 Actions") + dim(` (${results.results.actions.length})`));
|
|
450
|
+
for (const a of results.results.actions) {
|
|
451
|
+
const status = a.done ? chalk.green("\u2713") : chalk.gray("\u25CB");
|
|
452
|
+
console.log(` ${status} ${a.title}`);
|
|
453
|
+
console.log(` ${dim(`${a.projectName} \xB7 ${a.priority} \xB7 ${a.id.slice(0, 8)}`)}`);
|
|
454
|
+
}
|
|
455
|
+
console.log();
|
|
456
|
+
}
|
|
457
|
+
if (results.results.notes.length > 0) {
|
|
458
|
+
console.log(amber("\u{1F4DD} Notes") + dim(` (${results.results.notes.length})`));
|
|
459
|
+
for (const n of results.results.notes) {
|
|
460
|
+
console.log(` \u2022 ${n.preview}`);
|
|
461
|
+
console.log(` ${dim(`${n.projectName} \xB7 ${n.id.slice(0, 8)}`)}`);
|
|
462
|
+
}
|
|
463
|
+
console.log();
|
|
464
|
+
}
|
|
465
|
+
if (results.results.decisions.length > 0) {
|
|
466
|
+
console.log(amber("\u{1F4CB} Decisions") + dim(` (${results.results.decisions.length})`));
|
|
467
|
+
for (const d of results.results.decisions) {
|
|
468
|
+
console.log(` \u2022 ${d.decision}`);
|
|
469
|
+
if (d.context) console.log(` ${dim(d.context)}`);
|
|
470
|
+
console.log(` ${dim(`${d.projectName} \xB7 ${d.id.slice(0, 8)}`)}`);
|
|
471
|
+
}
|
|
472
|
+
console.log();
|
|
473
|
+
}
|
|
474
|
+
} catch (e) {
|
|
475
|
+
handleError(e);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
var fix = program.command("fix").description("Fix data issues");
|
|
479
|
+
fix.command("duplicates").description("Fix duplicate action and project IDs").action(async () => {
|
|
480
|
+
try {
|
|
481
|
+
console.log(amber("\u{1F527} Fixing duplicate action IDs..."));
|
|
482
|
+
const actionResult = await fixDuplicateIds();
|
|
483
|
+
if (actionResult.totalFixed > 0) {
|
|
484
|
+
console.log(chalk.green("\u2713") + ` Fixed ${actionResult.totalFixed} duplicate action ID(s)`);
|
|
485
|
+
for (const f of actionResult.fixes) {
|
|
486
|
+
console.log(dim(` ${f.oldId?.slice(0, 8)} \u2192 ${f.newId?.slice(0, 8)} (${f.title})`));
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
console.log(chalk.green("\u2713") + " No duplicate action IDs found");
|
|
490
|
+
}
|
|
491
|
+
console.log(amber("\n\u{1F527} Fixing duplicate project IDs..."));
|
|
492
|
+
const projectResult = await fixDuplicateProjectIds();
|
|
493
|
+
if (projectResult.totalFixed > 0) {
|
|
494
|
+
console.log(chalk.green("\u2713") + ` Fixed ${projectResult.totalFixed} duplicate project ID(s)`);
|
|
495
|
+
for (const f of projectResult.fixes) {
|
|
496
|
+
console.log(dim(` ${f.oldId?.slice(0, 8)} \u2192 ${f.newId?.slice(0, 8)} (${f.name})`));
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
console.log(chalk.green("\u2713") + " No duplicate project IDs found");
|
|
500
|
+
}
|
|
501
|
+
} catch (e) {
|
|
502
|
+
handleError(e);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
fix.command("normalize").description("Normalize statuses and categories to Title Case").action(async () => {
|
|
506
|
+
try {
|
|
507
|
+
console.log(amber("\u{1F527} Normalizing statuses and categories..."));
|
|
508
|
+
const result = await normalize();
|
|
509
|
+
const total = result.statusFixes + result.categoryFixes + result.priorityFixes;
|
|
510
|
+
if (total > 0) {
|
|
511
|
+
console.log(chalk.green("\u2713") + ` Normalized ${total} field(s):`);
|
|
512
|
+
if (result.statusFixes > 0) {
|
|
513
|
+
console.log(dim(` Statuses: ${result.statusFixes}`));
|
|
514
|
+
for (const f of result.details.status) {
|
|
515
|
+
console.log(dim(` "${f.from}" \u2192 "${f.to}"`));
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (result.categoryFixes > 0) {
|
|
519
|
+
console.log(dim(` Categories: ${result.categoryFixes}`));
|
|
520
|
+
for (const f of result.details.category) {
|
|
521
|
+
console.log(dim(` "${f.from}" \u2192 "${f.to}"`));
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (result.priorityFixes > 0) {
|
|
525
|
+
console.log(dim(` Priorities: ${result.priorityFixes}`));
|
|
526
|
+
for (const f of result.details.priority) {
|
|
527
|
+
console.log(dim(` "${f.from}" \u2192 "${f.to}"`));
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
} else {
|
|
531
|
+
console.log(chalk.green("\u2713") + " Everything already normalized");
|
|
532
|
+
}
|
|
533
|
+
} catch (e) {
|
|
534
|
+
handleError(e);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
var exp = program.command("export").description("Export project data");
|
|
538
|
+
exp.command("json").description("Export all projects as JSON").option("-f, --file <path>", "Write to file instead of stdout").action(async (opts) => {
|
|
539
|
+
try {
|
|
540
|
+
const data = await exportData("json");
|
|
541
|
+
const output = JSON.stringify(data, null, 2);
|
|
542
|
+
if (opts.file) {
|
|
543
|
+
writeFileSync(opts.file, output);
|
|
544
|
+
console.log(chalk.green("\u2713") + ` Exported to ${opts.file}`);
|
|
545
|
+
} else {
|
|
546
|
+
console.log(output);
|
|
547
|
+
}
|
|
548
|
+
} catch (e) {
|
|
549
|
+
handleError(e);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
exp.command("csv").description("Export projects summary as CSV").option("-f, --file <path>", "Write to file instead of stdout").action(async (opts) => {
|
|
553
|
+
try {
|
|
554
|
+
const data = await exportData("csv");
|
|
555
|
+
if (opts.file) {
|
|
556
|
+
writeFileSync(opts.file, data);
|
|
557
|
+
console.log(chalk.green("\u2713") + ` Exported to ${opts.file}`);
|
|
558
|
+
} else {
|
|
559
|
+
console.log(data);
|
|
560
|
+
}
|
|
561
|
+
} catch (e) {
|
|
562
|
+
handleError(e);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
359
565
|
async function resolveProject(idOrPrefix) {
|
|
360
566
|
try {
|
|
361
567
|
return await getProject(idOrPrefix);
|