From 3970b6bcf9716e7e9f3b74b19dfa9cd44db0ce52 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:18:52 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20repertoire=20add=20=E3=81=AE=E3=83=91?= =?UTF-8?q?=E3=82=A4=E3=83=97=20stdin=20=E3=81=A7=E8=A4=87=E6=95=B0=20conf?= =?UTF-8?q?irm=20=E3=81=8C=E5=A4=B1=E6=95=97=E3=81=99=E3=82=8B=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh api の stdio を inherit→pipe に変更し stdin の消費を防止。 readline の内部バッファ消失を防ぐためシングルトン pipe line queue を導入。 --- src/commands/repertoire/add.ts | 5 +-- src/shared/prompt/confirm.ts | 57 ++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 21 deletions(-) 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'; }