[READ-ONLY] Mirror of https://github.com/bombshell-dev/clack. Effortlessly build beautiful command-line apps bomb.sh/docs/clack/basics/getting-started/
cli command-line command-line-app node prompt prompts
5

Configure Feed

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

fix(prompts): respect directory option in path prompt (#483)

authored by

paul valladares and committed by
GitHub
(Mar 9, 2026, 6:24 PM UTC) 134a1a1c 9786226e

+102 -21
+5
.changeset/big-pants-invite.md
··· 1 + --- 2 + "@clack/prompts": patch 3 + --- 4 + 5 + Fix the `path` prompt so `directory: true` correctly enforces directory-only selection while still allowing directory navigation, and add regression tests for both directory and default file selection behavior.
+1 -1
packages/prompts/src/path.ts
··· 63 63 }) 64 64 .filter( 65 65 ({ path, isDirectory }) => 66 - path.startsWith(userInput) && (opts.directory || !isDirectory) 66 + path.startsWith(userInput) && (isDirectory || !opts.directory) 67 67 ); 68 68 return items.map((item) => ({ 69 69 value: item.path,
+60 -20
packages/prompts/test/__snapshots__/path.test.ts.snap
··· 8 8 │ 9 9 │ Search: /tmp/█ 10 10 │ ● /tmp/bar 11 + │ ○ /tmp/foo 12 + │ ○ /tmp/hello 11 13 │ ○ /tmp/root.zip 12 14 │ ↑/↓ to select • Enter: confirm • Type: to search 13 15 └", 14 - "<cursor.backward count=999><cursor.up count=7>", 16 + "<cursor.backward count=999><cursor.up count=9>", 15 17 "<cursor.down count=1>", 16 18 "<erase.down>", 17 19 "■ foo ··· 30 32 │ 31 33 │ Search: /tmp/█ 32 34 │ ● /tmp/bar 35 + │ ○ /tmp/foo 36 + │ ○ /tmp/hello 33 37 │ ○ /tmp/root.zip 34 38 │ ↑/↓ to select • Enter: confirm • Type: to search 35 39 └", 36 - "<cursor.backward count=999><cursor.up count=7>", 40 + "<cursor.backward count=999><cursor.up count=9>", 37 41 "<cursor.down count=3>", 38 42 "<erase.down>", 39 43 "│ Search: /tmp/_█ ··· 57 61 │ 58 62 │ Search: /tmp/█ 59 63 │ ● /tmp/bar 64 + │ ○ /tmp/foo 65 + │ ○ /tmp/hello 60 66 │ ○ /tmp/root.zip 61 67 │ ↑/↓ to select • Enter: confirm • Type: to search 62 68 └", 63 - "<cursor.backward count=999><cursor.up count=7>", 69 + "<cursor.backward count=999><cursor.up count=9>", 64 70 "<cursor.down count=3>", 65 71 "<erase.down>", 66 72 "│ Search: /tmp/b█ ··· 107 113 │ 108 114 │ Search: /tmp/█ 109 115 │ ● /tmp/bar 116 + │ ○ /tmp/foo 117 + │ ○ /tmp/hello 110 118 │ ○ /tmp/root.zip 111 119 │ ↑/↓ to select • Enter: confirm • Type: to search 112 120 └", 113 - "<cursor.backward count=999><cursor.up count=7>", 121 + "<cursor.backward count=999><cursor.up count=9>", 114 122 "<cursor.down count=3>", 115 123 "<erase.down>", 116 124 "│ Search: /tmp/x█ ··· 141 149 │ 142 150 │ Search: /tmp/█ 143 151 │ ● /tmp/bar 152 + │ ○ /tmp/foo 153 + │ ○ /tmp/hello 144 154 │ ○ /tmp/root.zip 145 155 │ ↑/↓ to select • Enter: confirm • Type: to search 146 156 └", 147 - "<cursor.backward count=999><cursor.up count=7>", 157 + "<cursor.backward count=999><cursor.up count=9>", 148 158 "<cursor.down count=1>", 149 159 "<erase.down>", 150 160 "◇ foo ··· 163 173 │ 164 174 │ Search: /tmp/█ 165 175 │ ● /tmp/bar 176 + │ ○ /tmp/foo 177 + │ ○ /tmp/hello 166 178 │ ○ /tmp/root.zip 167 179 │ ↑/↓ to select • Enter: confirm • Type: to search 168 180 └", 169 - "<cursor.backward count=999><cursor.up count=7>", 181 + "<cursor.backward count=999><cursor.up count=9>", 170 182 "<cursor.down count=3>", 171 183 "<erase.down>", 172 184 "│ Search: /tmp/b█ ··· 197 209 │ 198 210 │ Search: /tmp/█ 199 211 │ ● /tmp/bar 212 + │ ○ /tmp/foo 213 + │ ○ /tmp/hello 200 214 │ ○ /tmp/root.zip 201 215 │ ↑/↓ to select • Enter: confirm • Type: to search 202 216 └", 203 - "<cursor.backward count=999><cursor.up count=7>", 217 + "<cursor.backward count=999><cursor.up count=9>", 204 218 "<cursor.down count=3>", 205 219 "<erase.down>", 206 220 "│ Search: /tmp/r█ ··· 224 238 │ 225 239 │ Search: /tmp/█ 226 240 │ ○ /tmp/bar 241 + │ ○ /tmp/foo 242 + │ ○ /tmp/hello 227 243 │ ● /tmp/root.zip 228 244 │ ↑/↓ to select • Enter: confirm • Type: to search 229 245 └", 230 - "<cursor.backward count=999><cursor.up count=7>", 246 + "<cursor.backward count=999><cursor.up count=9>", 231 247 "<cursor.down count=3>", 232 248 "<erase.down>", 233 249 "│ Search: /tmp/b█ ··· 253 269 │ 254 270 │ Search: /tmp/█ 255 271 │ ● /tmp/bar 272 + │ ○ /tmp/foo 273 + │ ○ /tmp/hello 256 274 │ ○ /tmp/root.zip 257 275 │ ↑/↓ to select • Enter: confirm • Type: to search 258 276 └", 259 - "<cursor.backward count=999><cursor.up count=7>", 277 + "<cursor.backward count=999><cursor.up count=9>", 260 278 "<cursor.down count=3>", 261 279 "<erase.down>", 262 280 "│ Search: /tmp/r█ ··· 280 298 │ 281 299 │ Search: /tmp/█ 282 300 │ ○ /tmp/bar 301 + │ ○ /tmp/foo 302 + │ ○ /tmp/hello 283 303 │ ● /tmp/root.zip 284 304 │ ↑/↓ to select • Enter: confirm • Type: to search 285 305 └", 286 - "<cursor.backward count=999><cursor.up count=7>", 306 + "<cursor.backward count=999><cursor.up count=9>", 287 307 "<cursor.down count=3>", 288 308 "<erase.down>", 289 309 "│ Search: /tmp/b█ ··· 309 329 │ 310 330 │ Search: /tmp/█ 311 331 │ ● /tmp/bar 332 + │ ○ /tmp/foo 333 + │ ○ /tmp/hello 312 334 │ ○ /tmp/root.zip 313 335 │ ↑/↓ to select • Enter: confirm • Type: to search 314 336 └", 315 - "<cursor.backward count=999><cursor.up count=7>", 337 + "<cursor.backward count=999><cursor.up count=9>", 316 338 "<cursor.down count=1>", 317 339 "<erase.down>", 318 340 "■ foo ··· 331 353 │ 332 354 │ Search: /tmp/█ 333 355 │ ● /tmp/bar 356 + │ ○ /tmp/foo 357 + │ ○ /tmp/hello 334 358 │ ○ /tmp/root.zip 335 359 │ ↑/↓ to select • Enter: confirm • Type: to search 336 360 └", 337 - "<cursor.backward count=999><cursor.up count=7>", 361 + "<cursor.backward count=999><cursor.up count=9>", 338 362 "<cursor.down count=3>", 339 363 "<erase.down>", 340 364 "│ Search: /tmp/_█ ··· 358 382 │ 359 383 │ Search: /tmp/█ 360 384 │ ● /tmp/bar 385 + │ ○ /tmp/foo 386 + │ ○ /tmp/hello 361 387 │ ○ /tmp/root.zip 362 388 │ ↑/↓ to select • Enter: confirm • Type: to search 363 389 └", 364 - "<cursor.backward count=999><cursor.up count=7>", 390 + "<cursor.backward count=999><cursor.up count=9>", 365 391 "<cursor.down count=3>", 366 392 "<erase.down>", 367 393 "│ Search: /tmp/b█ ··· 408 434 │ 409 435 │ Search: /tmp/█ 410 436 │ ● /tmp/bar 437 + │ ○ /tmp/foo 438 + │ ○ /tmp/hello 411 439 │ ○ /tmp/root.zip 412 440 │ ↑/↓ to select • Enter: confirm • Type: to search 413 441 └", 414 - "<cursor.backward count=999><cursor.up count=7>", 442 + "<cursor.backward count=999><cursor.up count=9>", 415 443 "<cursor.down count=3>", 416 444 "<erase.down>", 417 445 "│ Search: /tmp/x█ ··· 442 470 │ 443 471 │ Search: /tmp/█ 444 472 │ ● /tmp/bar 473 + │ ○ /tmp/foo 474 + │ ○ /tmp/hello 445 475 │ ○ /tmp/root.zip 446 476 │ ↑/↓ to select • Enter: confirm • Type: to search 447 477 └", 448 - "<cursor.backward count=999><cursor.up count=7>", 478 + "<cursor.backward count=999><cursor.up count=9>", 449 479 "<cursor.down count=1>", 450 480 "<erase.down>", 451 481 "◇ foo ··· 464 494 │ 465 495 │ Search: /tmp/█ 466 496 │ ● /tmp/bar 497 + │ ○ /tmp/foo 498 + │ ○ /tmp/hello 467 499 │ ○ /tmp/root.zip 468 500 │ ↑/↓ to select • Enter: confirm • Type: to search 469 501 └", 470 - "<cursor.backward count=999><cursor.up count=7>", 502 + "<cursor.backward count=999><cursor.up count=9>", 471 503 "<cursor.down count=3>", 472 504 "<erase.down>", 473 505 "│ Search: /tmp/b█ ··· 498 530 │ 499 531 │ Search: /tmp/█ 500 532 │ ● /tmp/bar 533 + │ ○ /tmp/foo 534 + │ ○ /tmp/hello 501 535 │ ○ /tmp/root.zip 502 536 │ ↑/↓ to select • Enter: confirm • Type: to search 503 537 └", 504 - "<cursor.backward count=999><cursor.up count=7>", 538 + "<cursor.backward count=999><cursor.up count=9>", 505 539 "<cursor.down count=3>", 506 540 "<erase.down>", 507 541 "│ Search: /tmp/r█ ··· 525 559 │ 526 560 │ Search: /tmp/█ 527 561 │ ○ /tmp/bar 562 + │ ○ /tmp/foo 563 + │ ○ /tmp/hello 528 564 │ ● /tmp/root.zip 529 565 │ ↑/↓ to select • Enter: confirm • Type: to search 530 566 └", 531 - "<cursor.backward count=999><cursor.up count=7>", 567 + "<cursor.backward count=999><cursor.up count=9>", 532 568 "<cursor.down count=3>", 533 569 "<erase.down>", 534 570 "│ Search: /tmp/b█ ··· 554 590 │ 555 591 │ Search: /tmp/█ 556 592 │ ● /tmp/bar 593 + │ ○ /tmp/foo 594 + │ ○ /tmp/hello 557 595 │ ○ /tmp/root.zip 558 596 │ ↑/↓ to select • Enter: confirm • Type: to search 559 597 └", 560 - "<cursor.backward count=999><cursor.up count=7>", 598 + "<cursor.backward count=999><cursor.up count=9>", 561 599 "<cursor.down count=3>", 562 600 "<erase.down>", 563 601 "│ Search: /tmp/r█ ··· 581 619 │ 582 620 │ Search: /tmp/█ 583 621 │ ○ /tmp/bar 622 + │ ○ /tmp/foo 623 + │ ○ /tmp/hello 584 624 │ ● /tmp/root.zip 585 625 │ ↑/↓ to select • Enter: confirm • Type: to search 586 626 └", 587 - "<cursor.backward count=999><cursor.up count=7>", 627 + "<cursor.backward count=999><cursor.up count=9>", 588 628 "<cursor.down count=3>", 589 629 "<erase.down>", 590 630 "│ Search: /tmp/b█
+36
packages/prompts/test/path.test.ts
··· 146 146 expect(output.buffer).toMatchSnapshot(); 147 147 }); 148 148 149 + test('directory mode only allows selecting directories', async () => { 150 + const result = prompts.path({ 151 + message: 'foo', 152 + root: '/tmp/', 153 + directory: true, 154 + input, 155 + output, 156 + }); 157 + 158 + input.emit('keypress', 'f', { name: 'f' }); 159 + input.emit('keypress', '', { name: 'return' }); 160 + 161 + const value = await result; 162 + 163 + expect(value).toBe('/tmp/foo'); 164 + }); 165 + 166 + test('default mode allows selecting files', async () => { 167 + const result = prompts.path({ 168 + message: 'foo', 169 + root: '/tmp/', 170 + input, 171 + output, 172 + }); 173 + 174 + input.emit('keypress', 'r', { name: 'r' }); 175 + input.emit('keypress', 'o', { name: 'o' }); 176 + input.emit('keypress', 'o', { name: 'o' }); 177 + input.emit('keypress', 't', { name: 't' }); 178 + input.emit('keypress', '', { name: 'return' }); 179 + 180 + const value = await result; 181 + 182 + expect(value).toBe('/tmp/root.zip'); 183 + }); 184 + 149 185 test('validation errors render and clear', async () => { 150 186 const result = prompts.path({ 151 187 message: 'foo',