From fb781977ce057d6a91017c6459a672226699c89e Mon Sep 17 00:00:00 2001 From: tupe12334 Date: Tue, 24 Feb 2026 03:47:52 +0200 Subject: [PATCH 1/2] refactor(utils): replace custom expandTilde with untildify package Replace the custom tilde expansion implementation in resolveProjectPath with the well-maintained untildify npm package (v6). - Removes custom code in favour of a focused, tested library - Improves cross-platform reliability (Windows, Linux, macOS) - Handles edge cases such as ~user paths on Unix systems - Simplifies the resolve-project-path module Closes #179 Co-Authored-By: Claude Sonnet 4.6 --- package.json | 3 +-- pnpm-lock.yaml | 27 +++++++++------------------ src/utils/resolve-project-path.ts | 16 +++------------- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 6fd2bbb..2e80040 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,6 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@cspell/dict-he": "^4.0.5", - "@types/async-retry": "^1.4.9", "@types/node": "^24.10.1", "@types/pluralize": "^0.0.33", "@types/update-notifier": "^6.0.8", @@ -112,9 +111,9 @@ "@grpc/proto-loader": "^0.7.13", "@oclif/core": "^4.8.0", "@oclif/plugin-help": "^6.2.36", - "async-retry": "^1.3.3", "fastest-levenshtein": "^1.0.16", "pluralize": "^8.0.0", + "untildify": "^6.0.0", "update-notifier": "^7.3.1", "uuid": "^13.0.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b27dc9d..25c8d12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,15 +20,15 @@ importers: '@oclif/plugin-help': specifier: ^6.2.36 version: 6.2.36 - async-retry: - specifier: ^1.3.3 - version: 1.3.3 fastest-levenshtein: specifier: ^1.0.16 version: 1.0.16 pluralize: specifier: ^8.0.0 version: 8.0.0 + untildify: + specifier: ^6.0.0 + version: 6.0.0 update-notifier: specifier: ^7.3.1 version: 7.3.1 @@ -48,9 +48,6 @@ importers: '@cspell/dict-he': specifier: ^4.0.5 version: 4.0.5 - '@types/async-retry': - specifier: ^1.4.9 - version: 1.4.9 '@types/node': specifier: ^24.10.1 version: 24.10.1 @@ -1855,9 +1852,6 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - '@types/async-retry@1.4.9': - resolution: {integrity: sha512-s1ciZQJzRh3708X/m3vPExr5KJlzlZJvXsKpbtE2luqNcbROr64qU+3KpJsYHqWMeaxI839OvXf9PrUSw1Xtyg==} - '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1898,9 +1892,6 @@ packages: '@types/pluralize@0.0.33': resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==} - '@types/retry@0.12.5': - resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==} - '@types/update-notifier@6.0.8': resolution: {integrity: sha512-IlDFnfSVfYQD+cKIg63DEXn3RFmd7W1iYtKQsJodcHK9R1yr8aKbKaPKfBxzPpcHCq2DU8zUq4PIPmy19Thjfg==} @@ -4806,6 +4797,10 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + untildify@6.0.0: + resolution: {integrity: sha512-sA2YTBvW2F463GvSbiZtso+dpuQV+B7xX9saX30SGrR5Fyx4AUcvA/zN+ShAkABKUKVyDaHECsJrHv5ToTuHsQ==} + engines: {node: '>=20'} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -7134,10 +7129,6 @@ snapshots: '@types/aria-query@5.0.4': {} - '@types/async-retry@1.4.9': - dependencies: - '@types/retry': 0.12.5 - '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -7177,8 +7168,6 @@ snapshots: '@types/pluralize@0.0.33': {} - '@types/retry@0.12.5': {} - '@types/update-notifier@6.0.8': dependencies: '@types/configstore': 6.0.2 @@ -10570,6 +10559,8 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + untildify@6.0.0: {} + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 diff --git a/src/utils/resolve-project-path.ts b/src/utils/resolve-project-path.ts index 0183f18..dbb3cc9 100644 --- a/src/utils/resolve-project-path.ts +++ b/src/utils/resolve-project-path.ts @@ -1,6 +1,6 @@ /* eslint-disable single-export/single-export */ -import { homedir } from 'node:os' -import { isAbsolute, join } from 'node:path' +import { isAbsolute } from 'node:path' +import untildify from 'untildify' import { daemonListProjects } from '../daemon/daemon-list-projects.js' /** @@ -26,16 +26,6 @@ function isPath(input: string): boolean { return false } -/** - * Expand tilde to home directory - */ -function expandTilde(input: string): string { - if (input.startsWith('~')) { - return join(homedir(), input.slice(1)) - } - return input -} - export class ProjectNotFoundError extends Error { constructor(projectName: string) { super( @@ -66,7 +56,7 @@ export async function resolveProjectPath( // 2. If it looks like a path, return it (with tilde expansion) if (isPath(input)) { - return expandTilde(input) + return untildify(input) } // 3. Otherwise treat as project name - look up in daemon From 6dd63b817e9b8d62afdecd71260fb98a7389da84 Mon Sep 17 00:00:00 2001 From: tupe12334 Date: Mon, 19 Oct 2026 22:55:29 +0300 Subject: [PATCH 2/2] test(utils): normalize tilde-expanded path for Windows untildify keeps the forward slash from the input, so compare normalized paths. Co-Authored-By: Claude Opus 5.5 --- src/utils/resolve-project-path.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/resolve-project-path.spec.ts b/src/utils/resolve-project-path.spec.ts index 91580f7..7f4f224 100644 --- a/src/utils/resolve-project-path.spec.ts +++ b/src/utils/resolve-project-path.spec.ts @@ -1,5 +1,5 @@ import { homedir } from 'node:os' -import { join } from 'node:path' +import { join, normalize } from 'node:path' import { describe, expect, it, vi, beforeEach } from 'vitest' const mockDaemonListProjects = vi.fn() @@ -33,7 +33,8 @@ describe('resolveProjectPath', () => { it('should expand tilde to home directory', async () => { const result = await resolveProjectPath('~/projects') - expect(result).toBe(join(homedir(), 'projects')) + // untildify keeps the input slash; normalize for Windows + expect(normalize(result)).toBe(join(homedir(), 'projects')) }) it('should return paths with slashes', async () => {