fix: Windows path support, error handling, and dedup in validators

- session-manager.js: fix getSessionStats path detection to handle
  Windows paths (C:\...) in addition to Unix paths (/)
- package-manager.js: add try-catch to setPreferredPackageManager for
  consistent error handling with setProjectPackageManager
- validate-hooks.js: extract duplicated hook entry validation into
  reusable validateHookEntry() helper
- Update .d.ts JSDoc for both fixes
This commit is contained in:
Affaan Mustafa
2026-02-12 15:57:20 -08:00
parent 76b271ab6b
commit 639c9aaca3
5 changed files with 51 additions and 44 deletions

View File

@@ -68,7 +68,7 @@ export function getPackageManager(options?: GetPackageManagerOptions): PackageMa
/**
* Set the user's globally preferred package manager.
* Saves to ~/.claude/package-manager.json.
* @throws If pmName is not a known package manager
* @throws If pmName is not a known package manager or if save fails
*/
export function setPreferredPackageManager(pmName: PackageManagerName): { packageManager: string; setAt: string };

View File

@@ -246,7 +246,12 @@ function setPreferredPackageManager(pmName) {
const config = loadConfig() || {};
config.packageManager = pmName;
config.setAt = new Date().toISOString();
saveConfig(config);
try {
saveConfig(config);
} catch (err) {
throw new Error(`Failed to save package manager preference: ${err.message}`);
}
return config;
}

View File

@@ -97,7 +97,8 @@ export function parseSessionMetadata(content: string | null): SessionMetadata;
/**
* Calculate statistics for a session.
* Accepts either a file path (ending in .tmp) or pre-read content string.
* Accepts either a file path (absolute, ending in .tmp) or pre-read content string.
* Supports both Unix (/path/to/session.tmp) and Windows (C:\path\to\session.tmp) paths.
*/
export function getSessionStats(sessionPathOrContent: string): SessionStats;

View File

@@ -146,12 +146,14 @@ function parseSessionMetadata(content) {
*/
function getSessionStats(sessionPathOrContent) {
// Accept pre-read content string to avoid redundant file reads.
// If the argument looks like a file path (no newlines, ends with .tmp),
// read from disk. Otherwise treat it as content.
const content = (typeof sessionPathOrContent === 'string' &&
// If the argument looks like a file path (no newlines, ends with .tmp,
// starts with / on Unix or drive letter on Windows), read from disk.
// Otherwise treat it as content.
const looksLikePath = typeof sessionPathOrContent === 'string' &&
!sessionPathOrContent.includes('\n') &&
sessionPathOrContent.startsWith('/') &&
sessionPathOrContent.endsWith('.tmp'))
sessionPathOrContent.endsWith('.tmp') &&
(sessionPathOrContent.startsWith('/') || /^[A-Za-z]:[/\\]/.test(sessionPathOrContent));
const content = looksLikePath
? getSessionContent(sessionPathOrContent)
: sessionPathOrContent;