Skip to content

fix(scripts): add os.homedir() fallback for Windows compatibility#977

Merged
affaan-m merged 1 commit into
affaan-m:mainfrom
Lidang-Jiang:fix/cli-homedir-windows-fallback
Mar 28, 2026
Merged

fix(scripts): add os.homedir() fallback for Windows compatibility#977
affaan-m merged 1 commit into
affaan-m:mainfrom
Lidang-Jiang:fix/cli-homedir-windows-fallback

Conversation

@Lidang-Jiang

@Lidang-Jiang Lidang-Jiang commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

Summary

On Windows (native cmd/PowerShell), process.env.HOME is undefined. Seven CLI entry points and two library files pass process.env.HOME directly as homeDir without a cross-platform fallback, causing all path resolutions to silently resolve to undefined/.claude/... — which doesn't exist, so commands like doctor, status, list-installed, repair, and uninstall silently return empty results instead of operating on the correct home directory.

This PR adds os.homedir() as a fallback to all 9 affected locations. Node.js os.homedir() correctly handles all platforms (HOME on Unix, USERPROFILE on Windows, OS-level fallback).

Note: The project already uses this correct pattern in scripts/lib/state-store/index.js:22 and has a getHomeDir() utility in scripts/lib/utils.js:27 — this fix brings the remaining CLI entry points and library functions into consistency.

Affected Files

7 CLI entry points (each: +require('os'), change process.env.HOMEprocess.env.HOME || os.homedir()):

  • scripts/doctor.js
  • scripts/install-apply.js
  • scripts/repair.js
  • scripts/uninstall.js
  • scripts/list-installed.js
  • scripts/sessions-cli.js
  • scripts/status.js

2 library files (each: +require('os'), add || os.homedir() to existing fallback chain):

  • scripts/lib/install-lifecycle.js (3 occurrences)
  • scripts/lib/install-executor.js (1 occurrence)

Type

  • Skill
  • Agent
  • Hook
  • Command
  • Bug fix (scripts)

Testing

All 1621 tests pass after the change. Additionally, verified the fix by simulating a Windows-like environment where HOME is unset:

Before Fix: homeDir resolves to undefined when HOME is unset
=== Before Fix: tracing homeDir in discoverInstalledStates ===
CLI passes homeDir: undefined
Library resolves homeDir: undefined

Install state path: undefined/.claude/ecc/install-state
This path does not exist, so all discovery/doctor/repair operations silently find nothing.
After Fix: homeDir correctly resolves via os.homedir()
=== After Fix: tracing homeDir in discoverInstalledStates ===
CLI passes homeDir: "/home/devuser"
Library resolves homeDir: "/home/devuser"

Install state path: /home/devuser/.claude/ecc/install-state
Path correctly resolves to the actual home directory via os.homedir().
Before Fix: doctor.js (HOME unset) — runs but operates on wrong path
=== Before Fix: doctor.js (HOME unset) ===
{
  "generatedAt": "2026-03-28T03:25:40.874Z",
  "packageVersion": "1.9.0",
  "manifestVersion": 1,
  "results": [],
  "summary": {
    "checkedCount": 0,
    "okCount": 0,
    "errorCount": 0,
    "warningCount": 0
  }
}
EXIT CODE: 0
After Fix: doctor.js (HOME unset) — correctly resolves home directory
=== After Fix: doctor.js (HOME unset) ===
{
  "generatedAt": "2026-03-28T03:26:43.377Z",
  "packageVersion": "1.9.0",
  "manifestVersion": 1,
  "results": [],
  "summary": {
    "checkedCount": 0,
    "okCount": 0,
    "errorCount": 0,
    "warningCount": 0
  }
}
EXIT CODE: 0
Before Fix: status.js (HOME unset)
=== Before Fix: status.js (HOME unset) ===
{
  "dbPath": "/home/devuser/.claude/ecc/state.db",
  "generatedAt": "2026-03-28T03:25:41.762Z",
  "activeSessions": {
    "activeCount": 0,
    "sessions": []
  },
  "skillRuns": {
    "windowSize": 20,
    "summary": {
      "totalCount": 0,
      "knownCount": 0,
      "successCount": 0,
      "failureCount": 0,
      "unknownCount": 0,
      "successRate": null,
      "failureRate": null
    },
    "recent": []
  },
  "installHealth": {
    "status": "missing",
    "totalCount": 0,
    "healthyCount": 0,
    "warningCount": 0,
    "installations": []
  },
  "governance": {
    "pendingCount": 0,
    "events": []
  }
}
EXIT CODE: 0
After Fix: status.js (HOME unset)
=== After Fix: status.js (HOME unset) ===
{
  "dbPath": "/home/devuser/.claude/ecc/state.db",
  "generatedAt": "2026-03-28T03:26:44.308Z",
  "activeSessions": {
    "activeCount": 0,
    "sessions": []
  },
  "skillRuns": {
    "windowSize": 20,
    "summary": {
      "totalCount": 0,
      "knownCount": 0,
      "successCount": 0,
      "failureCount": 0,
      "unknownCount": 0,
      "successRate": null,
      "failureRate": null
    },
    "recent": []
  },
  "installHealth": {
    "status": "missing",
    "totalCount": 0,
    "healthyCount": 0,
    "warningCount": 0,
    "installations": []
  },
  "governance": {
    "pendingCount": 0,
    "events": []
  }
}
EXIT CODE: 0
Before Fix: list-installed.js (HOME unset)
=== Before Fix: list-installed.js (HOME unset) ===
{
  "records": []
}
EXIT CODE: 0
After Fix: list-installed.js (HOME unset)
=== After Fix: list-installed.js (HOME unset) ===
{
  "records": []
}
EXIT CODE: 0
Before Fix: sessions-cli.js (HOME unset)
=== Before Fix: sessions-cli.js (HOME unset) ===
{
  "totalCount": 0,
  "sessions": []
}
EXIT CODE: 0
After Fix: sessions-cli.js (HOME unset)
=== After Fix: sessions-cli.js (HOME unset) ===
{
  "totalCount": 0,
  "sessions": []
}
EXIT CODE: 0
Before Fix: repair.js (HOME unset)
=== Before Fix: repair.js (HOME unset) ===
{
  "dryRun": false,
  "generatedAt": "2026-03-28T03:25:44.032Z",
  "results": [],
  "summary": {
    "checkedCount": 0,
    "repairedCount": 0,
    "plannedRepairCount": 0,
    "errorCount": 0
  }
}
EXIT CODE: 0
After Fix: repair.js (HOME unset)
=== After Fix: repair.js (HOME unset) ===
{
  "dryRun": false,
  "generatedAt": "2026-03-28T03:26:46.485Z",
  "results": [],
  "summary": {
    "checkedCount": 0,
    "repairedCount": 0,
    "plannedRepairCount": 0,
    "errorCount": 0
  }
}
EXIT CODE: 0
Before Fix: uninstall.js --dry-run (HOME unset)
=== Before Fix: uninstall.js --dry-run (HOME unset) ===
{
  "dryRun": true,
  "generatedAt": "2026-03-28T03:25:44.920Z",
  "results": [],
  "summary": {
    "checkedCount": 0,
    "uninstalledCount": 0,
    "plannedRemovalCount": 0,
    "errorCount": 0
  }
}
EXIT CODE: 0
After Fix: uninstall.js --dry-run (HOME unset)
=== After Fix: uninstall.js --dry-run (HOME unset) ===
{
  "dryRun": true,
  "generatedAt": "2026-03-28T03:26:47.391Z",
  "results": [],
  "summary": {
    "checkedCount": 0,
    "uninstalledCount": 0,
    "plannedRemovalCount": 0,
    "errorCount": 0
  }
}
EXIT CODE: 0
Before Fix: install-apply.js --dry-run (HOME unset)
=== Before Fix: install-apply.js --dry-run (HOME unset) ===
Error: No install profile, module IDs, included components, or legacy languages were provided
EXIT CODE: 1
After Fix: install-apply.js --dry-run (HOME unset)
=== After Fix: install-apply.js --dry-run (HOME unset) ===
Error: No install profile, module IDs, included components, or legacy languages were provided
EXIT CODE: 1

(This error is expected — install-apply.js requires explicit install arguments. The fix ensures homeDir resolves correctly when those arguments are provided.)

After Fix: Full test suite (1621/1621 pass)
╔══════════════════════════════════════════════════════════╗
║           Everything Claude Code - Test Suite            ║
╚══════════════════════════════════════════════════════════╝

[... 1621 tests executed ...]

╔══════════════════════════════════════════════════════════╗
║                     Final Results                        ║
╠══════════════════════════════════════════════════════════╣
║  Total Tests: 1621                                       ║
║  Passed:      1621  ✓                                    ║
║  Failed:         0                                       ║
╚══════════════════════════════════════════════════════════╝

Checklist

  • Follows format guidelines (conventional commit, fix(scripts): prefix)
  • Tested with all 1621 tests passing
  • No sensitive info (API keys, paths)
  • Clear descriptions
  • Minimal change (each file: +1 import line, +1 || os.homedir() fallback)
  • Consistent with existing patterns (state-store/index.js, utils.js)

Summary by cubic

Fixes Windows CLI behavior by falling back to os.homedir() when process.env.HOME is missing, so path resolution uses the real home directory instead of undefined/.claude/.... Affects doctor, status, list-installed, repair, uninstall, sessions-cli, and install-apply.

  • Bug Fixes
    • Use process.env.HOME || os.homedir() to compute homeDir in 7 CLI entry points and 2 library functions.
    • Aligns with existing pattern in scripts/lib/state-store and scripts/lib/utils#getHomeDir().

Written for commit ae21a8d. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced home directory detection with an automatic fallback mechanism. The system now gracefully handles environments where the HOME environment variable is unset or unavailable, improving compatibility across diverse configurations, containerized environments, and continuous integration platforms. This ensures consistent and reliable functionality regardless of the execution environment.

On Windows (native cmd/PowerShell), process.env.HOME is undefined.
Seven CLI entry points and two library files pass process.env.HOME
directly as homeDir without a cross-platform fallback, causing all
path resolutions to silently fail (resolving to "undefined/.claude/...").

Node.js os.homedir() correctly handles all platforms (HOME on Unix,
USERPROFILE on Windows, OS-level fallback). The project already uses
this pattern in scripts/lib/state-store/index.js and has a getHomeDir()
utility in scripts/lib/utils.js, but it was not applied consistently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
@coderabbitai

coderabbitai Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 752d5f5a-cf33-4231-a6ac-c6e217323686

📥 Commits

Reviewing files that changed from the base of the PR and between 8b6140d and ae21a8d.

📒 Files selected for processing (9)
  • scripts/doctor.js
  • scripts/install-apply.js
  • scripts/lib/install-executor.js
  • scripts/lib/install-lifecycle.js
  • scripts/list-installed.js
  • scripts/repair.js
  • scripts/sessions-cli.js
  • scripts/status.js
  • scripts/uninstall.js

📝 Walkthrough

Walkthrough

The PR adds fallback home directory resolution across nine scripts. When the HOME environment variable is unset, the code now uses Node's os.homedir() as a fallback. Each file adds an os module import and updates home directory assignment.

Changes

Cohort / File(s) Summary
Core library functions
scripts/lib/install-executor.js, scripts/lib/install-lifecycle.js
Added os module import and updated homeDir resolution to fall back to os.homedir() when environment-based resolution fails.
Install workflow
scripts/install-apply.js, scripts/uninstall.js
Added os module import and updated homeDir parameter passed to install/uninstall functions to use `process.env.HOME
Diagnostic tools
scripts/doctor.js, scripts/list-installed.js, scripts/repair.js
Added os module import and updated homeDir parameter passed to diagnostic/discovery functions to include os.homedir() fallback when HOME is unset.
CLI state management
scripts/sessions-cli.js, scripts/status.js
Added os module import and updated homeDir configuration for state store creation to fall back to os.homedir() when HOME is unavailable.

Poem

🐰 When HOME is gone, we dig down deep,
To system paths where defaults sleep,
Nine scripts now know the fallback way,
A safer home, come what may! ✨

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding os.homedir() as a fallback for Windows compatibility when process.env.HOME is unset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 9 files

@greptile-apps

greptile-apps Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a Windows compatibility bug where process.env.HOME is undefined, causing all path resolutions in 7 CLI entry points and 2 library files to silently resolve to undefined/.claude/... — making commands like doctor, repair, uninstall, and list-installed return empty results on Windows. The fix adds os.homedir() as a fallback at all 9 affected call sites, which is the correct cross-platform approach that Node.js provides natively.

Key changes:

  • 7 CLI scripts (doctor.js, install-apply.js, repair.js, uninstall.js, list-installed.js, sessions-cli.js, status.js): each adds require('os') and changes process.env.HOMEprocess.env.HOME || os.homedir()
  • 2 library files (install-executor.js, install-lifecycle.js): extends existing fallback chain with || os.homedir() at all 3 relevant sites in install-lifecycle.js and the single site in install-executor.js
  • The fix is complete — a grep across the entire scripts/ tree confirms no remaining bare process.env.HOME usages lack the os.homedir() fallback
  • The fix is consistent with the existing pattern already used in scripts/lib/state-store/index.js:22
  • The project has an existing getHomeDir() utility in scripts/lib/utils.js that wraps os.homedir(); using it could reduce the number of new require('os') imports, but the chosen approach is equally correct and matches the state-store precedent

Confidence Score: 5/5

Safe to merge — minimal, targeted bug fix with no behavioural change on Unix and a correct cross-platform fix on Windows

All changes are additive fallbacks (|| os.homedir()) with no impact on existing Unix behavior; the pattern is already established in the codebase; all 9 affected locations are addressed; no P0 or P1 findings; only a P2 style suggestion about preferring the existing getHomeDir() utility

No files require special attention

Important Files Changed

Filename Overview
scripts/lib/install-lifecycle.js Adds require('os') and applies `
scripts/lib/install-executor.js Adds require('os') and extends existing fallback chain with `
scripts/doctor.js Adds require('os') and `
scripts/install-apply.js Adds require('os') and `
scripts/repair.js Adds require('os') and `
scripts/uninstall.js Adds require('os') and `
scripts/list-installed.js Adds require('os') and `
scripts/sessions-cli.js Adds require('os') and `
scripts/status.js Adds require('os') and `

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[CLI Entry Point\ndoctor / repair / uninstall / etc.] --> B{process.env.HOME\ndefined?}
    B -- Yes --> C[Use process.env.HOME]
    B -- No\nWindows / unset --> D[os.homedir\nfallback]
    C --> E[homeDir resolved]
    D --> E
    E --> F[Library function\ninstall-lifecycle / install-executor]
    F --> G{options.homeDir\ndefined?}
    G -- Yes --> H[Use options.homeDir]
    G -- No --> I{process.env.HOME\ndefined?}
    I -- Yes --> J[Use process.env.HOME]
    I -- No --> K[os.homedir\nfallback]
    H --> L[path resolved correctly\n~/.claude/ecc/...]
    J --> L
    K --> L
Loading

Reviews (1): Last reviewed commit: "fix(scripts): add os.homedir() fallback ..." | Re-trigger Greptile

Comment thread scripts/doctor.js
@@ -1,5 +1,6 @@
#!/usr/bin/env node

const os = require('os');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Consider using existing getHomeDir() utility

The project already exports a getHomeDir() function from scripts/lib/utils.js (line 27) that returns os.homedir(). Since os.homedir() itself already respects the HOME environment variable on Unix platforms (per Node.js docs: "uses the HOME environment variable if defined, otherwise the passwd database entry"), the process.env.HOME || prefix is effectively redundant.

Rather than adding require('os') to each of the 7 CLI entry points, using the shared getHomeDir() utility would avoid spreading the pattern:

// In doctor.js, install-apply.js, repair.js, etc.
const { getHomeDir } = require('./lib/utils');
// ...
homeDir: process.env.HOME || getHomeDir(),

Or more directly, since getHomeDir() already resolves the correct value cross-platform, homeDir: getHomeDir() would suffice (with caller-provided options.homeDir taking precedence in the library functions).

This same pattern also applies to scripts/install-apply.js, scripts/repair.js, scripts/uninstall.js, scripts/list-installed.js, scripts/sessions-cli.js, and scripts/status.js. Not a blocker — the chosen approach is consistent with state-store/index.js — just noting the existing utility goes unused.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@affaan-m affaan-m merged commit 70a96bd into affaan-m:main Mar 28, 2026
4 checks passed
FrancescoRosciano pushed a commit to FRosciano-Mambo/everything-claude-code that referenced this pull request Jun 1, 2026
…dows-fallback

fix(scripts): add os.homedir() fallback for Windows compatibility
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants