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! :)
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

at f4056f7901fca651977b2b95bfefefe9e685a00a 100 lines 2.6 kB view raw
1import * as fs from 'node:fs/promises'; 2import * as process from 'node:process'; 3 4/** 5 * Read body content from various sources following GitHub CLI patterns 6 * 7 * @param bodyString - Direct body text from --body flag 8 * @param bodyFilePath - File path or '-' for stdin 9 * @returns Body content or undefined if no input provided 10 * @throws Error if both bodyString and bodyFilePath are provided 11 * @throws Error if file doesn't exist or cannot be read 12 */ 13export async function readBodyInput( 14 bodyString?: string, 15 bodyFilePath?: string, 16): Promise<string | undefined> { 17 // Error if both are provided 18 if (bodyString !== undefined && bodyFilePath !== undefined) { 19 throw new Error( 20 'Cannot specify both --body and --body-file. Choose one input method.', 21 ); 22 } 23 24 // Direct string input (including empty string) 25 if (bodyString !== undefined) { 26 return bodyString; 27 } 28 29 // File or stdin input 30 if (bodyFilePath) { 31 // Read from stdin 32 if (bodyFilePath === '-') { 33 return await readFromStdin(); 34 } 35 36 // Read from file 37 try { 38 const stats = await fs.stat(bodyFilePath); 39 40 if (stats.isDirectory()) { 41 throw new Error(`'${bodyFilePath}' is a directory, not a file`); 42 } 43 44 const content = await fs.readFile(bodyFilePath, 'utf-8'); 45 return content; 46 } catch (error) { 47 if (error instanceof Error) { 48 // Re-throw our custom directory error 49 if (error.message.includes('is a directory')) { 50 throw error; 51 } 52 53 // Handle ENOENT (file not found) 54 if ('code' in error && error.code === 'ENOENT') { 55 throw new Error(`File not found: ${bodyFilePath}`); 56 } 57 58 // Handle EACCES (permission denied) 59 if ('code' in error && error.code === 'EACCES') { 60 throw new Error(`Permission denied: ${bodyFilePath}`); 61 } 62 63 throw new Error( 64 `Failed to read file '${bodyFilePath}': ${error.message}`, 65 ); 66 } 67 68 throw new Error(`Failed to read file '${bodyFilePath}': Unknown error`); 69 } 70 } 71 72 // No input provided 73 return undefined; 74} 75 76/** 77 * Read content from stdin 78 * @returns Content from stdin as string 79 */ 80async function readFromStdin(): Promise<string> { 81 return new Promise((resolve, reject) => { 82 const chunks: Buffer[] = []; 83 84 process.stdin.on('data', (chunk: Buffer) => { 85 chunks.push(chunk); 86 }); 87 88 process.stdin.on('end', () => { 89 const content = Buffer.concat(chunks).toString('utf-8'); 90 resolve(content); 91 }); 92 93 process.stdin.on('error', (error: Error) => { 94 reject(new Error(`Failed to read from stdin: ${error.message}`)); 95 }); 96 97 // Resume stdin in case it's paused 98 process.stdin.resume(); 99 }); 100}