diff --git a/scripts/doctor.js b/scripts/doctor.js index 80505d3f6..7b0cd04af 100644 --- a/scripts/doctor.js +++ b/scripts/doctor.js @@ -96,6 +96,7 @@ function main() { const report = buildDoctorReport({ repoRoot: require('path').join(__dirname, '..'), homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), targets: options.targets, }); diff --git a/scripts/install-apply.js b/scripts/install-apply.js index 26c5be1c4..97d8279c9 100755 --- a/scripts/install-apply.js +++ b/scripts/install-apply.js @@ -164,6 +164,7 @@ async function main() { const rawPlan = createInstallPlanFromRequest(request, { projectRoot: process.cwd(), homeDir: process.env.HOME || os.homedir(), + env: process.env, claudeRulesDir: process.env.CLAUDE_RULES_DIR || null, }); diff --git a/scripts/lib/install-executor.js b/scripts/lib/install-executor.js index e5405cf2a..31ee46874 100644 --- a/scripts/lib/install-executor.js +++ b/scripts/lib/install-executor.js @@ -7,6 +7,7 @@ const { toCursorAgentRelativePath } = require('./cursor-agent-names'); const { LEGACY_INSTALL_TARGETS, parseInstallArgs } = require('./install/request'); const { SUPPORTED_INSTALL_TARGETS, listLegacyCompatibilityLanguages, resolveLegacyCompatibilitySelection, resolveInstallPlan } = require('./install-manifests'); const { getInstallTargetAdapter } = require('./install-targets/registry'); +const { resolveInvocationEnvironment } = require('./invocation-environment'); const LANGUAGE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/; const CLAUDE_ECC_NAMESPACE = 'ecc'; @@ -645,7 +646,7 @@ function createLegacyCompatInstallPlan(options = {}) { sourceRoot, projectRoot, homeDir: options.homeDir, - env: options.env || process.env, + env: resolveInvocationEnvironment(options), target, profileId: null, moduleIds: selection.moduleIds, @@ -775,6 +776,7 @@ function createManifestInstallPlan(options = {}) { repoRoot: sourceRoot, projectRoot, homeDir: options.homeDir, + env: resolveInvocationEnvironment(options), profileId: options.profileId || null, moduleIds: options.moduleIds || [], includeComponentIds: options.includeComponentIds || [], diff --git a/scripts/lib/install-lifecycle.js b/scripts/lib/install-lifecycle.js index 31828f02b..54bae3e8b 100644 --- a/scripts/lib/install-lifecycle.js +++ b/scripts/lib/install-lifecycle.js @@ -22,6 +22,7 @@ const { const { adaptAntigravityAgent } = require('./install/antigravity-agent'); const { buildInstallIndex, rewriteRelativeLinks } = require('./install/link-rewrite'); const { getInstallTargetAdapter, listInstallTargetAdapters } = require('./install-targets/registry'); +const { resolveInvocationEnvironment } = require('./invocation-environment'); const OPENCODE_BUILD_ARTIFACT = path.join('.opencode', 'dist'); const OPENCODE_BUILD_SCRIPT = path.join('scripts', 'build-opencode.js'); const OPENCODE_PLUGIN_NOT_BUILT_CODE = 'opencode-plugin-not-built'; @@ -1275,7 +1276,7 @@ function discoverInstalledStates(options = {}) { const context = { homeDir: options.homeDir || process.env.HOME || os.homedir(), projectRoot: options.projectRoot || process.cwd(), - env: options.env || process.env, + env: resolveInvocationEnvironment(options), }; const targets = normalizeTargets(options.targets); @@ -1546,13 +1547,13 @@ function buildDoctorReport(options = {}) { homeDir: options.homeDir, projectRoot: options.projectRoot, targets: options.targets, - env: options.env, + env: resolveInvocationEnvironment(options), }).filter(record => record.exists); const context = { repoRoot, homeDir: options.homeDir || process.env.HOME || os.homedir(), projectRoot: options.projectRoot || process.cwd(), - env: options.env || process.env, + env: resolveInvocationEnvironment(options), manifestVersion: manifests.modulesVersion, packageVersion: readPackageVersion(repoRoot) }; @@ -1718,7 +1719,7 @@ function repairInstalledStates(options = {}) { repoRoot, homeDir: options.homeDir || process.env.HOME || os.homedir(), projectRoot: options.projectRoot || process.cwd(), - env: options.env || process.env, + env: resolveInvocationEnvironment(options), manifestVersion: manifests.modulesVersion, packageVersion: readPackageVersion(repoRoot) }; @@ -2045,7 +2046,7 @@ function uninstallInstalledStates(options = {}) { homeDir: options.homeDir, projectRoot: options.projectRoot, targets: options.targets, - env: options.env, + env: resolveInvocationEnvironment(options), }).filter(record => record.exists); const results = records.map(record => { diff --git a/scripts/lib/install-manifests.js b/scripts/lib/install-manifests.js index be3421b27..eeeb3afe1 100644 --- a/scripts/lib/install-manifests.js +++ b/scripts/lib/install-manifests.js @@ -2,6 +2,7 @@ const fs = require('fs'); const os = require('os'); const path = require('path'); const { getInstallTargetAdapter, planInstallTargetScaffold } = require('./install-targets/registry'); +const { resolveInvocationEnvironment } = require('./invocation-environment'); const DEFAULT_REPO_ROOT = path.join(__dirname, '../..'); const SUPPORTED_INSTALL_TARGETS = ['claude', 'claude-project', 'cursor', 'antigravity', 'codex', 'gemini', 'opencode', 'codebuddy', 'joycode', 'qwen', 'zed', 'hermes', 'openclaw', 'kimi']; @@ -595,7 +596,7 @@ function resolveInstallPlan(options = {}) { repoRoot: manifests.repoRoot, projectRoot: validatedProjectRoot || manifests.repoRoot, homeDir: validatedHomeDir || os.homedir(), - env: options.env || process.env, + env: resolveInvocationEnvironment(options), } : null; const targetAdapter = target ? getInstallTargetAdapter(target) : null; diff --git a/scripts/lib/install-state-store-sync.js b/scripts/lib/install-state-store-sync.js index aa8fb6325..fb31c6aa0 100644 --- a/scripts/lib/install-state-store-sync.js +++ b/scripts/lib/install-state-store-sync.js @@ -51,6 +51,7 @@ async function reconcileCanonicalInstallStates(options = {}) { homeDir: options.homeDir, projectRoot: options.projectRoot, targets: options.targets, + env: options.env, discoverInstalledStates: options.discoverInstalledStates, })); } diff --git a/scripts/lib/install-targets/registry.js b/scripts/lib/install-targets/registry.js index 368e1cfe5..6861a63e9 100644 --- a/scripts/lib/install-targets/registry.js +++ b/scripts/lib/install-targets/registry.js @@ -12,6 +12,7 @@ const openclawHome = require('./openclaw-home'); const opencodeHome = require('./opencode-home'); const qwenHome = require('./qwen-home'); const zedProject = require('./zed-project'); +const { resolveInvocationEnvironment } = require('../invocation-environment'); const ADAPTERS = Object.freeze([ claudeHome, @@ -52,7 +53,7 @@ function planInstallTargetScaffold(options = {}) { repoRoot: options.repoRoot, projectRoot: options.projectRoot || options.repoRoot, homeDir: options.homeDir, - env: options.env || process.env, + env: resolveInvocationEnvironment(options), }; const validationIssues = adapter.validate(planningInput); const blockingIssues = validationIssues.filter(issue => ( diff --git a/scripts/lib/install/runtime.js b/scripts/lib/install/runtime.js index 55f55bfbd..1342814fb 100644 --- a/scripts/lib/install/runtime.js +++ b/scripts/lib/install/runtime.js @@ -5,6 +5,7 @@ const { createLegacyInstallPlan, createManifestInstallPlan, } = require('../install-executor'); +const { resolveInvocationEnvironment } = require('../invocation-environment'); function createInstallPlanFromRequest(request, options = {}) { if (!request || typeof request !== 'object') { @@ -20,6 +21,7 @@ function createInstallPlanFromRequest(request, options = {}) { excludeComponentIds: request.excludeComponentIds, projectRoot: options.projectRoot, homeDir: options.homeDir, + env: resolveInvocationEnvironment(options), sourceRoot: options.sourceRoot, }); } @@ -32,6 +34,7 @@ function createInstallPlanFromRequest(request, options = {}) { excludeComponentIds: request.excludeComponentIds, projectRoot: options.projectRoot, homeDir: options.homeDir, + env: resolveInvocationEnvironment(options), claudeRulesDir: options.claudeRulesDir, sourceRoot: options.sourceRoot, }); diff --git a/scripts/lib/invocation-environment.js b/scripts/lib/invocation-environment.js new file mode 100644 index 000000000..33cf45dab --- /dev/null +++ b/scripts/lib/invocation-environment.js @@ -0,0 +1,17 @@ +'use strict'; + +function resolveInvocationEnvironment(options = {}) { + if (Object.prototype.hasOwnProperty.call(options, 'env')) { + return options.env || {}; + } + + if (typeof options.homeDir === 'string' && options.homeDir.trim() !== '') { + return {}; + } + + return process.env; +} + +module.exports = { + resolveInvocationEnvironment, +}; diff --git a/scripts/lib/mcp-inventory/readers/opencode.js b/scripts/lib/mcp-inventory/readers/opencode.js index c85b89a31..c19cd1f87 100644 --- a/scripts/lib/mcp-inventory/readers/opencode.js +++ b/scripts/lib/mcp-inventory/readers/opencode.js @@ -4,6 +4,7 @@ const fs = require('fs'); const os = require('os'); const path = require('path'); const { resolveOpencodeConfigRoot } = require('../../opencode-paths'); +const { resolveInvocationEnvironment } = require('../../invocation-environment'); // OpenCode stores MCP servers under "mcp" in its resolved configuration root. // Shape differs from Claude/Codex: @@ -39,7 +40,10 @@ function mapOpencodeServer(name, raw, configPath) { function readOpencodeMcp(options = {}) { const homeDir = options.homeDir || os.homedir(); - const configRoot = resolveOpencodeConfigRoot({ homeDir, env: options.env }); + const configRoot = resolveOpencodeConfigRoot({ + homeDir, + env: resolveInvocationEnvironment(options), + }); const candidatePaths = options.configPath ? [options.configPath] : [ diff --git a/scripts/lib/opencode-paths.js b/scripts/lib/opencode-paths.js index c80cb3ca4..0a5ef3f3d 100644 --- a/scripts/lib/opencode-paths.js +++ b/scripts/lib/opencode-paths.js @@ -2,6 +2,7 @@ const os = require('os'); const path = require('path'); +const { resolveInvocationEnvironment } = require('./invocation-environment'); function configuredDirectory(environment, name) { const value = environment && environment[name]; @@ -11,7 +12,7 @@ function configuredDirectory(environment, name) { } function resolveOpencodeConfigRoot(options = {}) { - const environment = options.env || process.env; + const environment = resolveInvocationEnvironment(options); const explicitRoot = configuredDirectory(environment, 'OPENCODE_CONFIG_DIR'); if (explicitRoot) { return explicitRoot; diff --git a/scripts/lib/state-store/install-state-projection.js b/scripts/lib/state-store/install-state-projection.js index 14a007c33..d63ba7911 100644 --- a/scripts/lib/state-store/install-state-projection.js +++ b/scripts/lib/state-store/install-state-projection.js @@ -317,6 +317,7 @@ function reconcileCurrentInstallState(store, options = {}) { homeDir: options.homeDir, projectRoot: options.projectRoot, targets: options.targets, + env: options.env, }); let result = reconcileInstallStateProjections(store, records); try { diff --git a/scripts/list-installed.js b/scripts/list-installed.js index a3f070bf6..4b9418c99 100644 --- a/scripts/list-installed.js +++ b/scripts/list-installed.js @@ -72,6 +72,7 @@ function main() { const records = discoverInstalledStates({ homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), targets: options.targets, }).filter(record => record.exists); diff --git a/scripts/repair.js b/scripts/repair.js index 34f614229..3494f1ade 100644 --- a/scripts/repair.js +++ b/scripts/repair.js @@ -81,6 +81,7 @@ async function main() { const result = repairInstalledStates({ repoRoot: require('path').join(__dirname, '..'), homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), targets: options.targets, dryRun: options.dryRun, @@ -89,6 +90,7 @@ async function main() { const { reconcileCanonicalInstallStates } = require('./lib/install-state-store-sync'); result.installStateProjection = await reconcileCanonicalInstallStates({ homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), targets: options.targets, }); diff --git a/scripts/status.js b/scripts/status.js index 0a1a3d84a..7f6404a12 100644 --- a/scripts/status.js +++ b/scripts/status.js @@ -467,6 +467,7 @@ async function main() { const installStateProjection = reconcileCurrentInstallState(store, { homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), }); const storedStatus = store.getStatus({ diff --git a/scripts/uninstall.js b/scripts/uninstall.js index 49df98d61..abeb2efa8 100644 --- a/scripts/uninstall.js +++ b/scripts/uninstall.js @@ -141,6 +141,7 @@ async function main() { } else { result = uninstallInstalledStates({ homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), targets: options.targets, dryRun: options.dryRun, @@ -162,6 +163,7 @@ async function main() { const { reconcileCanonicalInstallStates } = require('./lib/install-state-store-sync'); result.installStateProjection = await reconcileCanonicalInstallStates({ homeDir: process.env.HOME || os.homedir(), + env: process.env, projectRoot: process.cwd(), targets: options.targets, });