Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ export default defineConfig({
| `-e,` `--env-file` `<path|false>` | Env file path, or "false" to disable env file loading |
| `--config` `<path>` | Config file path (default: auto-detect) |
| `-p,` `--prefix` | Prefixed output mode (no TUI, for CI/scripts) |
| `--only` `<a,b,...>` | Only run these processes (+ their dependencies) |
| `--exclude` `<a,b,...>` | Exclude these processes |
| `-o,` `--only` `<a,b,...>` | Only run these processes (+ their dependencies) |
| `-x,` `--exclude` `<a,b,...>` | Exclude these processes |
| `--kill-others` | Kill all processes when any exits (regardless of exit code) |
| `--kill-others-on-fail` | Kill all processes when any exits with non-zero code |
| `--max-restarts` `<n>` | Max auto-restarts for crashed processes |
Expand Down
2 changes: 2 additions & 0 deletions src/cli-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const FLAGS: FlagDef[] = [
{
type: 'value',
long: '--only',
short: '-o',
key: 'only',
description: 'Only run these processes (+ their dependencies)',
valueName: '<a,b,...>',
Expand All @@ -147,6 +148,7 @@ export const FLAGS: FlagDef[] = [
{
type: 'value',
long: '--exclude',
short: '-x',
key: 'exclude',
description: 'Exclude these processes',
valueName: '<a,b,...>',
Expand Down
36 changes: 36 additions & 0 deletions src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,48 @@ describe('parseArgs', () => {
expect(result.exclude).toEqual(['migrate'])
})

test('-o is short for --only', () => {
const result = parseArgs(argv('-o', 'api,web'))
expect(result.only).toEqual(['api', 'web'])
})

test('-x is short for --exclude', () => {
const result = parseArgs(argv('-x', 'migrate'))
expect(result.exclude).toEqual(['migrate'])
})

test('repeated --only merges values', () => {
const result = parseArgs(argv('--only', 'api', '--only', 'web'))
expect(result.only).toEqual(['api', 'web'])
})

test('repeated -o merges comma-separated values', () => {
const result = parseArgs(argv('-o', 'api,db', '-o', 'web'))
expect(result.only).toEqual(['api', 'db', 'web'])
})

test('repeated --exclude merges values', () => {
const result = parseArgs(argv('--exclude', 'db', '-x', 'migrate'))
expect(result.exclude).toEqual(['db', 'migrate'])
})

test('mixed short and long flags merge', () => {
const result = parseArgs(argv('-o', 'api', '--only', 'web'))
expect(result.only).toEqual(['api', 'web'])
})

test('--only and --exclude can coexist', () => {
const result = parseArgs(argv('--only', 'api,web,db', '--exclude', 'db'))
expect(result.only).toEqual(['api', 'web', 'db'])
expect(result.exclude).toEqual(['db'])
})

test('-o and -x can coexist', () => {
const result = parseArgs(argv('-o', 'api,web', '-x', 'web'))
expect(result.only).toEqual(['api', 'web'])
expect(result.exclude).toEqual(['web'])
})

test('init sets init flag', () => {
expect(parseArgs(argv('init')).init).toBe(true)
})
Expand Down
3 changes: 2 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export function parseArgs(argv: string[]): ParsedArgs {
const value = flag.parse ? flag.parse(next, arg) : next
const current = (result as any)[flag.key]
if (Array.isArray(current)) {
current.push(value)
if (Array.isArray(value)) current.push(...value)
else current.push(value)
} else {
;(result as any)[flag.key] = value
}
Expand Down
36 changes: 36 additions & 0 deletions src/ui/prefix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,42 @@ describe('PrefixDisplay (integration)', () => {
expect(exitCode).toBe(1)
}, 10000)

test('-o filters to named process and its deps', async () => {
const config = writeConfig(
'only-filter.json',
JSON.stringify({
processes: {
db: { command: "echo 'db ready'" },
api: { command: "echo 'api ready'", dependsOn: ['db'] },
web: { command: "echo 'web ready'" }
}
})
)
const { stdout, exitCode } = await runPrefix(config, ['-o', 'api'])
expect(stdout).toContain('[api]')
expect(stdout).toContain('[db]')
expect(stdout).not.toContain('[web]')
expect(exitCode).toBe(0)
}, 10000)

test('repeated -o merges filters', async () => {
const config = writeConfig(
'only-repeated.json',
JSON.stringify({
processes: {
a: { command: "echo 'a'" },
b: { command: "echo 'b'" },
c: { command: "echo 'c'" }
}
})
)
const { stdout, exitCode } = await runPrefix(config, ['-o', 'a', '-o', 'b'])
expect(stdout).toContain('[a]')
expect(stdout).toContain('[b]')
expect(stdout).not.toContain('[c]')
expect(exitCode).toBe(0)
}, 10000)

test('cursor-up sequences are stripped to preserve prefix', async () => {
// Use bun -e to emit real escape bytes — avoids printf portability issues
const config = writeConfig(
Expand Down
Loading