WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage from it! :)
1/**
2 * Format a date for display with human-readable relative time
3 * @param dateString - ISO date string
4 * @returns Human-readable date string
5 */
6export function formatDate(dateString: string): string {
7 const date = new Date(dateString);
8 const now = new Date();
9 const diff = now.getTime() - date.getTime();
10 const days = Math.floor(diff / (1000 * 60 * 60 * 24));
11
12 if (days === 0) return 'today';
13 if (days === 1) return 'yesterday';
14 if (days < 7) return `${days} days ago`;
15 if (days < 30) return `${Math.floor(days / 7)} weeks ago`;
16 if (days < 365) return `${Math.floor(days / 30)} months ago`;
17 return date.toLocaleDateString();
18}
19
20/**
21 * Format issue state for display
22 * @param state - Issue state ('open' or 'closed')
23 * @returns Formatted state badge string
24 */
25export function formatIssueState(state: 'open' | 'closed'): string {
26 return state === 'open' ? '[OPEN]' : '[CLOSED]';
27}
28
29/**
30 * Pick specific fields from an object, omitting fields not present in the object
31 */
32function pickFields(obj: Record<string, unknown>, fields: string[]): Record<string, unknown> {
33 const result: Record<string, unknown> = {};
34 for (const field of fields) {
35 if (field in obj) {
36 result[field] = obj[field];
37 }
38 }
39 return result;
40}
41
42/**
43 * Output data as JSON to stdout, following GitHub CLI conventions.
44 *
45 * @param data - The data to output (object or array of objects)
46 * @param fields - Comma-separated field names to include; omit for all fields
47 */
48export function outputJson<T extends object>(data: T | T[], fields?: string): void {
49 if (fields) {
50 const fieldList = fields
51 .split(',')
52 .map((f) => f.trim())
53 .filter(Boolean);
54 if (Array.isArray(data)) {
55 console.log(
56 JSON.stringify(
57 data.map((item) => pickFields(item as Record<string, unknown>, fieldList)),
58 null,
59 2
60 )
61 );
62 } else {
63 console.log(JSON.stringify(pickFields(data as Record<string, unknown>, fieldList), null, 2));
64 }
65 } else {
66 console.log(JSON.stringify(data, null, 2));
67 }
68}