From 01c877664633d3edf9cfb203c04e1b42080bbb6f Mon Sep 17 00:00:00 2001 From: Eivind Date: Wed, 26 Aug 2026 00:55:47 +0200 Subject: [PATCH 1/2] fix(config): only pass -- to npm when forwarding script args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Script patterns with extra args (`'npm:codegen:* --project-id abc'`) always inserted a `--` separator before the args. npm needs it, since it swallows anything after the script name. yarn and pnpm forward the args as-is and pass the `--` through to the script, where Go/cobra CLIs like `supabase` read every following flag as a positional arg: yarn run codegen:ts -- --project-id abc → supabase: "Must specify one of --local, --linked, --project-id, or --db-url" Measured against npm 11, yarn 4, pnpm 10 and bun 1.3: only npm needs the separator, and bun accepts either form. So emit it for npm alone. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 1 + README.md | 4 +++- src/config/expand-scripts.test.ts | 28 ++++++++++++++++++++++++++++ src/config/expand-scripts.ts | 13 ++++++++++--- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8a9f5c3..04bd2fa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,6 +40,7 @@ src/ - `T` toggles an `HH:mm:ss.SSS` timestamp gutter in TUI mode; also enabled via `timestamps: true` config or `--timestamps` flag. Accepts a format string (e.g. `timestamps: "HH:mm:ss"`) with tokens: `YYYY`, `MM`, `DD`, `HH`, `hh`, `mm`, `ss`, `SSS`, `A` - Compact keybinding hints in the status bar; `H` or `?` opens a full help overlay. Config lives in `src/ui/keybindings.ts` - `autowrap: false` config opts into disabling the host terminal's autowrap (DECAWM, `\x1b[?7l`) at startup, restored (`?7h`) on shutdown (`App.disableAutowrap()`). Default is `true` (untouched). Works around an OpenTUI renderer bug: its incremental diff positions every run with an absolute cursor move and never relies on autowrap, but it also never disables it and skips its right-edge cursor re-home to avoid tripping a wrap — so a run that fills the last column wraps into column 1 of the next row, smearing right-pane output into the tab sidebar. Because the diff records the *intended* cell (`syncCell`), the stray cells never repaint until a full repaint (resize). Pane content still wraps inside its own VT grid — only the host emit-cursor wrap is turned off. Off by default because it changes a global terminal mode; the proper fix is upstream ([anomalyco/opentui#1187](https://github.com/anomalyco/opentui/issues/1187)) +- Script-pattern extra args (`'npm:lint:* --fix'`): only npm gets a `--` separator. yarn and pnpm forward the args as-is and would pass a literal `--` to the script, which breaks flag parsing in Go/cobra CLIs like `supabase`; bun accepts either form - Set `interactive: true` on processes that need stdin (REPLs, shells) - Non-interactive panes hide the terminal cursor (shown during input mode) - Set `errorMatcher: true` to detect ANSI red output, or a regex string to match custom patterns — shows a red indicator on the tab while the process keeps running diff --git a/README.md b/README.md index d48609e..8a83408 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,9 @@ scripts that have sub-scripts beneath them. E.g. if `format:check` has `format:check` but keeps the leaf scripts. **Extra args:** Anything after the first space in the pattern is forwarded -as extra arguments to each matched command: `lint:* --fix` → `bun run lint:js -- --fix`. +as extra arguments to each matched command: `lint:* --fix` → `bun run lint:js --fix`. +npm is the only package manager that needs a `--` separator, so it gets one: +`npm run lint:js -- --fix`. **Template inheritance:** Config properties on a pattern entry (color, env, dependsOn, etc.) are inherited by all expanded processes. Color arrays are diff --git a/src/config/expand-scripts.test.ts b/src/config/expand-scripts.test.ts index 9e9bda0..b24cbb7 100644 --- a/src/config/expand-scripts.test.ts +++ b/src/config/expand-scripts.test.ts @@ -406,6 +406,34 @@ describe('expandScriptPatterns', () => { expect(proc(result, 'ts').command).toBe('npm run lint:ts -- --fix') }) + test('yarn gets no -- separator (it would reach the script as a literal arg)', () => { + const dir = setupDir('args-yarn', { + 'package.json': pkgJson({ 'codegen:ts': 'gen-types', 'codegen:go': 'gen-go' }), + 'yarn.lock': '' + }) + const result = expandScriptPatterns({ processes: { 'npm:codegen:* --project-id abc': {} } }, dir) + expect(proc(result, 'ts').command).toBe('yarn run codegen:ts --project-id abc') + expect(proc(result, 'go').command).toBe('yarn run codegen:go --project-id abc') + }) + + test('pnpm gets no -- separator', () => { + const dir = setupDir('args-pnpm', { + 'package.json': pkgJson({ lint: 'eslint' }), + 'pnpm-lock.yaml': '' + }) + const result = expandScriptPatterns({ processes: { 'npm:lint --fix': {} } }, dir) + expect(proc(result, 'lint').command).toBe('pnpm run lint --fix') + }) + + test('bun gets no -- separator', () => { + const dir = setupDir('args-bun', { + 'package.json': pkgJson({ lint: 'eslint' }), + 'bun.lock': '' + }) + const result = expandScriptPatterns({ processes: { 'npm:lint --fix': {} } }, dir) + expect(proc(result, 'lint').command).toBe('bun run lint --fix') + }) + test('multiple extra args forwarded', () => { const dir = setupDir('args-multi', { 'package.json': pkgJson({ 'lint:js': 'eslint' }) diff --git a/src/config/expand-scripts.ts b/src/config/expand-scripts.ts index 47cb580..f966bfb 100644 --- a/src/config/expand-scripts.ts +++ b/src/config/expand-scripts.ts @@ -75,11 +75,16 @@ function splitPatternArgs(raw: string): { glob: string; extraArgs: string } { return { glob: raw.slice(0, i), extraArgs: raw.slice(i) } } -/** Convert a script name (with optional extra args) into a ` run