diff --git a/src/commands/repertoire/add.ts b/src/commands/repertoire/add.ts index 29bdf65..66cf97e 100644 --- a/src/commands/repertoire/add.ts +++ b/src/commands/repertoire/add.ts @@ -70,7 +70,7 @@ export async function repertoireAddCommand(spec: string): Promise { 'api', `/repos/${owner}/${repo}/tarball/${ref}`, ], - { stdio: ['inherit', 'pipe', 'pipe'] }, + { stdio: ['pipe', 'pipe', 'pipe'] }, ); writeFileSync(tmpTarPath, tarballBuffer); @@ -160,8 +160,9 @@ export async function repertoireAddCommand(spec: string): Promise { const packageDir = getRepertoirePackageDir(owner, repo); if (existsSync(packageDir)) { + info(`⚠ パッケージ @${owner}/${repo} は既にインストールされています`); const overwrite = await confirm( - `${owner}/${repo} は既にインストールされています。上書きしますか?`, + '上書きしますか?', false, ); if (!overwrite) { diff --git a/src/shared/prompt/confirm.ts b/src/shared/prompt/confirm.ts index 7450c60..f0a3c11 100644 --- a/src/shared/prompt/confirm.ts +++ b/src/shared/prompt/confirm.ts @@ -98,7 +98,8 @@ export async function confirm(message: string, defaultYes = true): Promise { - const rl = readline.createInterface({ input: process.stdin }); +/** + * Shared pipe reader singleton. + * + * readline.createInterface buffers data from stdin internally. + * Creating and closing multiple interfaces loses buffered lines. + * This singleton reads all lines once and serves them as a queue. + */ +let pipeLineQueue: string[] | null = null; +let pipeQueueReady: Promise | null = null; - return new Promise((resolve) => { - let resolved = false; +function ensurePipeQueue(): Promise { + if (pipeQueueReady) return pipeQueueReady; - rl.once('line', (line) => { - resolved = true; - rl.close(); - pauseStdinSafely(); - const trimmed = line.trim().toLowerCase(); - if (!trimmed) { - resolve(defaultYes); - return; - } - resolve(trimmed === 'y' || trimmed === 'yes'); + pipeQueueReady = new Promise((resolve) => { + const lines: string[] = []; + const rl = readline.createInterface({ input: process.stdin }); + + rl.on('line', (line) => { + lines.push(line); }); - rl.once('close', () => { - if (!resolved) { - resolve(defaultYes); - } + rl.on('close', () => { + pipeLineQueue = lines; + resolve(); }); }); + + return pipeQueueReady; +} + +async function readConfirmFromPipe(defaultYes: boolean): Promise { + await ensurePipeQueue(); + + const line = pipeLineQueue!.shift(); + if (line === undefined) { + return defaultYes; + } + const trimmed = line.trim().toLowerCase(); + if (!trimmed) { + return defaultYes; + } + return trimmed === 'y' || trimmed === 'yes'; }