Skip to content

feat(dbt-tools): auto-discover config with Windows compatibility#464

Merged
anandgupta42 merged 6 commits intomainfrom
fix/dbt-config
Mar 26, 2026
Merged

feat(dbt-tools): auto-discover config with Windows compatibility#464
anandgupta42 merged 6 commits intomainfrom
fix/dbt-config

Conversation

@mdesmet
Copy link
Contributor

@mdesmet mdesmet commented Mar 25, 2026

What does this PR do?

Auto-discover dbt project root and Python path when no config file exists, removing the requirement to run altimate-dbt init before using any command. Adds full Windows compatibility for Python and dbt binary resolution.

Changes:

  • config.ts: add findProjectRoot() and discoverPython() (exported), update read() to auto-discover projectRoot and pythonPath at runtime when no config file exists. Gracefully handles malformed config JSON by falling back to auto-discovery.
  • discoverPython() checks project-local venvs → VIRTUAL_ENVCONDA_PREFIXwhich/where python; tries python3 before python on Unix, python.exe on Windows
  • dbt-resolve.ts: platform-aware resolution using Scripts/ vs bin/ and .exe suffix on Windows; where instead of which on Windows; pyenv/asdf shim resolution skipped on Windows
  • init.ts: remove duplicated find()/python() helpers; import from config.ts

Type of change

  • New feature (non-breaking change which adds functionality)
  • Refactoring (no functional changes)

Issue for this PR

Improves developer experience — no linked issue.

How did you verify your code works?

  • 8 new unit tests covering auto-discovery, findProjectRoot, discoverPython, malformed config fallback
  • All 71 tests pass (5 pre-existing e2e failures unrelated to this PR)
  • Full typecheck passes (0 errors)
  • Marker Guard passes
  • Prettier passes
  • 6-model consensus code review completed

Checklist

  • Tests added/updated
  • Code reviewed (6-model consensus)
  • Prettier formatting verified

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

Summary by CodeRabbit

  • New Features

    • Auto-discovers dbt project root and Python executable when not explicitly configured
    • Enhanced Windows platform support for dbt resolution and discovery
  • Bug Fixes

    • Gracefully handles missing or malformed configuration files by falling back to auto-discovery

…s for vscode integration

- config.ts: add findProjectRoot() and discoverPython() (exported), update read() to auto-discover
  projectRoot and pythonPath at runtime when no config file exists — removes requirement to run
  `altimate-dbt init` before using any command
- discoverPython() prioritises ALTIMATE_CODE_VIRTUAL_ENV (injected by vscode-altimate-mcp-server)
  over project-local venvs; tries python3 before python in each candidate location
- dbt-resolve.ts: add tier 2 (ALTIMATE_CODE_PYTHON_PATH sibling) and tier 6 (ALTIMATE_CODE_VIRTUAL_ENV)
  so the dbt binary is found when altimate serve is spawned with a vscode-activated Python environment
- init.ts: remove duplicated find()/python() helpers; import from config.ts
- tests: add config.test.ts coverage for auto-discovery, findProjectRoot, discoverPython; add
  dbt-resolve.test.ts scenarios for ALTIMATE_CODE_PYTHON_PATH and ALTIMATE_CODE_VIRTUAL_ENV priority

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7601d85b-baff-4c03-94d8-e25378a6f4b6

📥 Commits

Reviewing files that changed from the base of the PR and between 492b8b8 and 04dfb31.

📒 Files selected for processing (2)
  • packages/dbt-tools/src/config.ts
  • packages/dbt-tools/src/dbt-resolve.ts

Disabled knowledge base sources:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Walkthrough

Replaced local init discovery with shared findProjectRoot and discoverPython in config; read() now auto-discovers project root and python when dbt.json is missing or malformed; dbt resolution gained platform-aware Windows/Unix probing; tests added/adjusted for discovery and resolution behaviors.

Changes

Cohort / File(s) Summary
Config utilities
packages/dbt-tools/src/config.ts
Added exported `findProjectRoot(start?: string): string
Init command
packages/dbt-tools/src/commands/init.ts
Removed local find() and python() helpers; now uses findProjectRoot(process.cwd()) and discoverPython(project) when flags are absent; cleaned unused imports.
DBT resolver
packages/dbt-tools/src/dbt-resolve.ts
Made resolution platform-aware (compute VENV_BIN/EXE), added Windows-specific candidate paths (e.g., Scripts/dbt.exe), skip pyenv/asdf shim checks on Windows, use where vs which, adjust PATH delimiter usage, and return platform-appropriate final fallback (dbt/dbt.exe).
Tests
packages/dbt-tools/test/config.test.ts, packages/dbt-tools/test/dbt-resolve.test.ts
config.test.ts: scope read() to test CWD; add tests for auto-discovery, malformed-config fallback, findProjectRoot traversal, and discoverPython priority. dbt-resolve.test.ts: minor formatting changes; cleanup and small comment rename. Added test helpers (mkdir, writeFile, realpath assertions).

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI (init)
  participant Config as config module
  participant FS as Filesystem
  participant Env as Environment

  CLI->>Config: init() / read()
  Config->>FS: findProjectRoot(start=CWD) — check for dbt_project.yml up tree
  alt projectRoot found
    Config->>Env: inspect env vars (VIRTUAL_ENV, CONDA_PREFIX, ALTIMATE_CODE_PYTHON_PATH)
    Config->>FS: probe project-local venvs (.venv, venv, env) for platform bin/Scripts
    Config->>FS: fallback to which/where python3|python
    Config-->>CLI: return synthesized config with projectRoot and pythonPath
  else
    Config-->>CLI: return null
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I hopped through folders, sniffed the hidden root,
Moved clever bits to config — tidy, resolute,
I chased python venvs, found Windows and Unix trails,
Updated tests to prove my rabbit tales,
🥕 A small refactor feast — now off for snacks!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers main objectives but omits the template's 'Test Plan' section and lacks detail on testing methodology despite claiming 8 new tests. Add a 'Test Plan' section explaining how the changes were tested (unit tests, e2e coverage, manual verification steps). Clarify the discrepancy between '8 new tests' mentioned and the '14 new tests' in PR objectives.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main changes: adding auto-discovery for dbt configuration and implementing Windows compatibility. Both are primary objectives evident in the raw_summary across config.ts, dbt-resolve.ts, and init.ts.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dbt-config

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/dbt-tools/src/dbt-resolve.ts (2)

165-177: Duplicate comment numbering for tiers 8 and 9.

Lines 132 and 165 both say // 8., and lines 150 and 177 both say // 9.. After inserting the new tiers, the comment numbers need to be updated to reflect the actual priority order from the docstring (which goes up to 10).

✏️ Suggested fix
-  // 8. `which dbt` on current PATH (catches pipx ~/.local/bin, system pip, homebrew, etc.)
+  // 9. `which dbt` on current PATH (catches pipx ~/.local/bin, system pip, homebrew, etc.)
   try {
     const whichDbt = execFileSync("which", ["dbt"], {
...
   } catch {}

-  // 9. Common known locations (last resort)
+  // 10. Common known locations (last resort)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/dbt-resolve.ts` around lines 165 - 177, The inline
tier comment numbers are duplicated after adding new detection tiers; update the
sequential comment markers to match the docstring priority (ensure comments for
the blocks that currently show "// 8." and "// 9." are renumbered correctly up
through "// 10.") so the ordering around the execFileSync("which", ["dbt"], ...)
block and the subsequent "Common known locations" block reflects their true
priority; locate these comments near the candidates.push calls in dbt-resolve.ts
and adjust the numeric labels to the correct tier numbers.

91-97: Comment numbering is stale after insertion of new tiers.

The comment on line 91 says // 3. but based on the priority list in the docstring (lines 44-54), this should be // 4. since tiers 2 and 3 are now ALTIMATE_CODE_PYTHON_PATH sibling and pythonPath sibling respectively.

✏️ Suggested fix
-  // 3. Project-local .venv/bin/dbt (uv, pdm, venv, poetry in-project, rye)
+  // 4. Project-local .venv/bin/dbt (uv, pdm, venv, poetry in-project, rye)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/dbt-resolve.ts` around lines 91 - 97, Update the
inline comment above the project-local venv loop from "// 3." to "// 4." so the
numbering matches the priority list in the docstring; locate the block that
checks "if (projectRoot) { for (const venvDir of [".venv", "venv", "env"]) { ...
} }" (references: projectRoot, venvDir, join, candidates) and change the numeric
comment label accordingly.
packages/dbt-tools/test/config.test.ts (1)

56-90: Consider test isolation with process.chdir() and module caching.

Using process.chdir() can cause issues if tests run in parallel, as the current working directory is process-global. While you correctly restore it in the finally block, any test running concurrently would be affected.

Additionally, the dynamic await import("../src/config") pattern may not provide fresh module state due to module caching—subsequent imports return the same cached module instance.

If you observe flaky tests, consider:

  1. Using jest.isolateModules() equivalent in Bun, or
  2. Refactoring the functions to accept cwd as a parameter for testability
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/test/config.test.ts` around lines 56 - 90, The tests
mutate process.cwd() and rely on a dynamic import of the module-level read
function, which can cause cross-test interference and stale module state; update
the tests (or the implementation) so they don't rely on a global chdir and
cached module state: either (A) refactor the read function in src/config to
accept an optional cwd/projectRoot parameter and call read(dir) from the tests
instead of using process.chdir, or (B) wrap the dynamic import and invocation in
an isolation mechanism (e.g., Bun/Jest isolateModules or explicitly clear the
module cache before importing) so each test gets a fresh module instance; locate
the read export in src/config and the two tests here to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/dbt-tools/src/dbt-resolve.ts`:
- Around line 44-54: Priority mismatch: resolveDbt() currently checks
project-local .venv before ALTIMATE_CODE_VIRTUAL_ENV, which can cause dbt to be
resolved to a different Python than discoverPython(). Update resolveDbt() so its
lookup order aligns with discoverPython(): keep ALTIMATE_DBT_PATH highest, then
check ALTIMATE_CODE_PYTHON_PATH sibling and pythonPath sibling, then honor
ALTIMATE_CODE_VIRTUAL_ENV (env var) before falling back to project-local .venv,
then VIRTUAL_ENV and CONDA_PREFIX; adjust the code paths in resolveDbt() and its
comment block accordingly (references: resolveDbt(), discoverPython(),
ALTIMATE_CODE_VIRTUAL_ENV, ALTIMATE_CODE_PYTHON_PATH, project-local .venv).

---

Nitpick comments:
In `@packages/dbt-tools/src/dbt-resolve.ts`:
- Around line 165-177: The inline tier comment numbers are duplicated after
adding new detection tiers; update the sequential comment markers to match the
docstring priority (ensure comments for the blocks that currently show "// 8."
and "// 9." are renumbered correctly up through "// 10.") so the ordering around
the execFileSync("which", ["dbt"], ...) block and the subsequent "Common known
locations" block reflects their true priority; locate these comments near the
candidates.push calls in dbt-resolve.ts and adjust the numeric labels to the
correct tier numbers.
- Around line 91-97: Update the inline comment above the project-local venv loop
from "// 3." to "// 4." so the numbering matches the priority list in the
docstring; locate the block that checks "if (projectRoot) { for (const venvDir
of [".venv", "venv", "env"]) { ... } }" (references: projectRoot, venvDir, join,
candidates) and change the numeric comment label accordingly.

In `@packages/dbt-tools/test/config.test.ts`:
- Around line 56-90: The tests mutate process.cwd() and rely on a dynamic import
of the module-level read function, which can cause cross-test interference and
stale module state; update the tests (or the implementation) so they don't rely
on a global chdir and cached module state: either (A) refactor the read function
in src/config to accept an optional cwd/projectRoot parameter and call read(dir)
from the tests instead of using process.chdir, or (B) wrap the dynamic import
and invocation in an isolation mechanism (e.g., Bun/Jest isolateModules or
explicitly clear the module cache before importing) so each test gets a fresh
module instance; locate the read export in src/config and the two tests here to
apply the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 183bb31a-8db2-403b-b865-335d8db67044

📥 Commits

Reviewing files that changed from the base of the PR and between ad99c5c and b4b402b.

📒 Files selected for processing (5)
  • packages/dbt-tools/src/commands/init.ts
  • packages/dbt-tools/src/config.ts
  • packages/dbt-tools/src/dbt-resolve.ts
  • packages/dbt-tools/test/config.test.ts
  • packages/dbt-tools/test/dbt-resolve.test.ts

- Add `ALTIMATE_CODE_PYTHON_PATH` as highest priority in `discoverPython()`
  so VS Code's explicit Python selection is not silently ignored
- Align `ALTIMATE_CODE_VIRTUAL_ENV` priority: move to tier 4 in `resolveDbt()`
  (before project-local `.venv`) to match `discoverPython()` priority and
  prevent Python/dbt resolving from different environments
- Add `JSON.parse` error handling in `read()` — malformed config file now
  falls back to auto-discovery instead of crashing
- Fix stale inline comment numbering in `dbt-resolve.ts` (1-11)
- Fix environment-dependent `read returns null` test by setting CWD to temp dir
- Add tests: `ALTIMATE_CODE_PYTHON_PATH` priority, malformed config fallback
- Run prettier on `dbt-resolve.ts` and `dbt-resolve.test.ts`

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/dbt-tools/src/dbt-resolve.ts (1)

114-131: ⚠️ Potential issue | 🟠 Major

Align VIRTUAL_ENV vs CONDA_PREFIX order with discoverPython().

Line 114-131 still prefers CONDA_PREFIX before VIRTUAL_ENV, while discoverPython() does the opposite. This can resolve dbt from a different environment than the selected Python.

🔧 Proposed fix
-  // 6. CONDA_PREFIX (conda/mamba/micromamba — set after `conda activate`)
-  const condaPrefix = process.env.CONDA_PREFIX
-  if (condaPrefix) {
-    candidates.push({
-      path: join(condaPrefix, "bin", "dbt"),
-      source: `CONDA_PREFIX (${condaPrefix})`,
-      binDir: join(condaPrefix, "bin"),
-    })
-  }
-
-  // 7. VIRTUAL_ENV (set by venv/virtualenv activate scripts)
+  // 6. VIRTUAL_ENV (set by venv/virtualenv activate scripts)
   const virtualEnv = process.env.VIRTUAL_ENV
   if (virtualEnv) {
     candidates.push({
       path: join(virtualEnv, "bin", "dbt"),
       source: `VIRTUAL_ENV (${virtualEnv})`,
       binDir: join(virtualEnv, "bin"),
     })
   }
+
+  // 7. CONDA_PREFIX (conda/mamba/micromamba — set after `conda activate`)
+  const condaPrefix = process.env.CONDA_PREFIX
+  if (condaPrefix) {
+    candidates.push({
+      path: join(condaPrefix, "bin", "dbt"),
+      source: `CONDA_PREFIX (${condaPrefix})`,
+      binDir: join(condaPrefix, "bin"),
+    })
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/dbt-resolve.ts` around lines 114 - 131, The current
resolution prefers CONDA_PREFIX before VIRTUAL_ENV (variables condaPrefix and
virtualEnv), which is inconsistent with discoverPython(); swap the order so
VIRTUAL_ENV is checked and pushed into candidates before CONDA_PREFIX to ensure
the virtualenv-discovered Python and dbt are resolved from the same environment
(update the block that constructs candidates so the virtualEnv block runs prior
to the condaPrefix block).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/dbt-tools/src/config.ts`:
- Around line 94-95: The code currently returns JSON.parse(raw) as Config
without runtime validation; after parsing the raw string, validate the resulting
object’s shape (required fields like projectRoot and dbtIntegration and their
expected types/enum values) before returning it from the function that contains
"JSON.parse(raw) as Config"; if validation fails, throw a clear error in the
catch/validation path instead of returning the unchecked object. Use a runtime
schema validator (e.g., zod) or explicit property/type checks against the Config
interface, and replace the blind cast with the validated Config instance so
downstream consumers receive a correct shape.
- Line 43: The Windows path/command assumptions must be removed: update
configDir() to use os.homedir() (or homedir()) instead of process.env.HOME, and
in discoverPython() replace POSIX-only checks (e.g., "bin/..." and `which`) with
platform-aware logic that also checks Windows locations ("Scripts\\...") and
uses `where` on Windows; additionally, when handling altPython (the variable
referenced in the line with `if (altPython && existsSync(altPython)) return
altPython`) validate that the path is an executable file (not just exists)
before returning it, and update other discovery steps (the calls that check
`bin/...`, `which`, and similar paths) to perform the same executable validation
and platform branching so discovery works on both POSIX and Windows.

In `@packages/dbt-tools/src/dbt-resolve.ts`:
- Line 210: The PATH concatenation on the env creation line can produce
"PATH=<bin>:undefined" when process.env.PATH is missing; update the construction
around resolved.binDir (the const env assignment that currently uses
`${resolved.binDir}:${process.env.PATH}`) to guard against a missing PATH by
using the same nullish coalescing approach as buildDbtEnv (e.g., use
process.env.PATH ?? '' or equivalent) so the PATH becomes
`${resolved.binDir}:${process.env.PATH ?? ''}` when resolved.binDir is present,
otherwise keep process.env unchanged.

---

Outside diff comments:
In `@packages/dbt-tools/src/dbt-resolve.ts`:
- Around line 114-131: The current resolution prefers CONDA_PREFIX before
VIRTUAL_ENV (variables condaPrefix and virtualEnv), which is inconsistent with
discoverPython(); swap the order so VIRTUAL_ENV is checked and pushed into
candidates before CONDA_PREFIX to ensure the virtualenv-discovered Python and
dbt are resolved from the same environment (update the block that constructs
candidates so the virtualEnv block runs prior to the condaPrefix block).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4eb605ba-2a98-4296-8d9a-64f3fbabc0ee

📥 Commits

Reviewing files that changed from the base of the PR and between b4b402b and fdb8519.

📒 Files selected for processing (4)
  • packages/dbt-tools/src/config.ts
  • packages/dbt-tools/src/dbt-resolve.ts
  • packages/dbt-tools/test/config.test.ts
  • packages/dbt-tools/test/dbt-resolve.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/dbt-tools/test/config.test.ts

export function discoverPython(projectRoot: string): string {
// ALTIMATE_CODE_PYTHON_PATH (explicit selection from vscode-altimate-mcp-server — highest priority)
const altPython = process.env.ALTIMATE_CODE_PYTHON_PATH
if (altPython && existsSync(altPython)) return altPython
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether the repo expects Windows/cross-platform behavior and where POSIX-only assumptions exist.
rg -n -C2 '(Windows|win32|cross[- ]platform|VS Code)' README.md packages/dbt-tools
rg -n -C2 'which\\(|bin/python3|bin/python|Scripts/python.exe|process\\.platform|win32' packages/dbt-tools/src packages/dbt-tools/test

Repository: AltimateAI/altimate-code

Length of output: 808


🏁 Script executed:

cat packages/dbt-tools/src/config.ts

Repository: AltimateAI/altimate-code

Length of output: 3447


🏁 Script executed:

# Check project structure and platform-specific considerations
fd -t f "(package.json|tsconfig|README)" packages/dbt-tools --max-depth 2

Repository: AltimateAI/altimate-code

Length of output: 132


🏁 Script executed:

# Search for any existing platform-specific code in the project
rg -n "process\.platform|win32|Scripts" packages/dbt-tools/src --max-count 10

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Check if the project has any Windows/cross-platform references
rg -n "(Windows|cross.platform|Cursor|Claude Code)" README.md --max-count 5

Repository: AltimateAI/altimate-code

Length of output: 579


Fix Windows path assumptions in Python discovery and config directory handling.

The discoverPython() function hardcodes POSIX-specific paths (bin/...) and commands (which), breaking on Windows which uses Scripts/... and where. Additionally, configDir() (line 18) relies on process.env.HOME, which is not set on Windows—use homedir() directly instead. Line 43 should also validate that the path is an executable file, not just that it exists.

Affected lines: 18, 43, 49, 57, 66, 75, 83.

Suggested patch
 function configDir() {
-  return join(process.env.HOME || homedir(), ".altimate-code")
+  return join(homedir(), ".altimate-code")
 }

 export function discoverPython(projectRoot: string): string {
+  const isWin = process.platform === "win32"
+  const binDir = isWin ? "Scripts" : "bin"
+  const candidates = isWin ? ["python.exe", "python3.exe", "python", "python3"] : ["python3", "python"]

   // ALTIMATE_CODE_PYTHON_PATH (explicit selection from vscode-altimate-mcp-server — highest priority)
   const altPython = process.env.ALTIMATE_CODE_PYTHON_PATH
   if (altPython && existsSync(altPython)) return altPython

   // ALTIMATE_CODE_VIRTUAL_ENV (injected by vscode-altimate-mcp-server — explicit user selection wins)
   const altVenv = process.env.ALTIMATE_CODE_VIRTUAL_ENV
   if (altVenv) {
-    for (const bin of ["python3", "python"]) {
-      const py = join(altVenv, "bin", bin)
+    for (const bin of candidates) {
+      const py = join(altVenv, binDir, bin)
       if (existsSync(py)) return py
     }
   }

   // Project-local venvs (uv, pdm, venv, poetry in-project, rye)
   for (const venvDir of [".venv", "venv", "env"]) {
-    for (const bin of ["python3", "python"]) {
-      const py = join(projectRoot, venvDir, "bin", bin)
+    for (const bin of candidates) {
+      const py = join(projectRoot, venvDir, binDir, bin)
       if (existsSync(py)) return py
     }
   }

   // VIRTUAL_ENV (set by activate scripts)
   const virtualEnv = process.env.VIRTUAL_ENV
   if (virtualEnv) {
-    for (const bin of ["python3", "python"]) {
-      const py = join(virtualEnv, "bin", bin)
+    for (const bin of candidates) {
+      const py = join(virtualEnv, binDir, bin)
       if (existsSync(py)) return py
     }
   }

   // CONDA_PREFIX
   const condaPrefix = process.env.CONDA_PREFIX
   if (condaPrefix) {
-    for (const bin of ["python3", "python"]) {
-      const py = join(condaPrefix, "bin", bin)
+    for (const bin of candidates) {
+      const py = join(condaPrefix, binDir, bin)
       if (existsSync(py)) return py
     }
   }

   // PATH-based discovery
-  for (const cmd of ["python3", "python"]) {
+  for (const cmd of isWin ? ["python", "py"] : ["python3", "python"]) {
     try {
-      return execFileSync("which", [cmd], { encoding: "utf-8" }).trim()
+      const locator = isWin ? "where" : "which"
+      return execFileSync(locator, [cmd], { encoding: "utf-8" }).trim().split(/\r?\n/)[0]!
     } catch {}
   }
   return "python3"
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/config.ts` at line 43, The Windows path/command
assumptions must be removed: update configDir() to use os.homedir() (or
homedir()) instead of process.env.HOME, and in discoverPython() replace
POSIX-only checks (e.g., "bin/..." and `which`) with platform-aware logic that
also checks Windows locations ("Scripts\\...") and uses `where` on Windows;
additionally, when handling altPython (the variable referenced in the line with
`if (altPython && existsSync(altPython)) return altPython`) validate that the
path is an executable file (not just exists) before returning it, and update
other discovery steps (the calls that check `bin/...`, `which`, and similar
paths) to perform the same executable validation and platform branching so
discovery works on both POSIX and Windows.

Comment on lines +94 to +95
return JSON.parse(raw) as Config
} catch {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate parsed config shape before returning it.

JSON.parse success does not guarantee a valid Config. A malformed-but-valid object (missing projectRoot, wrong dbtIntegration, etc.) will propagate and can break downstream consumers.

Suggested patch
 type Config = {
   projectRoot: string
   pythonPath: string
   dbtIntegration: string
   queryLimit: number
 }
+
+function isConfig(value: unknown): value is Config {
+  if (!value || typeof value !== "object") return false
+  const v = value as Record<string, unknown>
+  return (
+    typeof v.projectRoot === "string" &&
+    typeof v.pythonPath === "string" &&
+    typeof v.dbtIntegration === "string" &&
+    typeof v.queryLimit === "number"
+  )
+}

 async function read(): Promise<Config | null> {
   const p = configPath()
   if (existsSync(p)) {
     try {
       const raw = await readFile(p, "utf-8")
-      return JSON.parse(raw) as Config
+      const parsed: unknown = JSON.parse(raw)
+      if (isConfig(parsed)) return parsed
     } catch {
       // Malformed config — fall through to auto-discovery
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/config.ts` around lines 94 - 95, The code currently
returns JSON.parse(raw) as Config without runtime validation; after parsing the
raw string, validate the resulting object’s shape (required fields like
projectRoot and dbtIntegration and their expected types/enum values) before
returning it from the function that contains "JSON.parse(raw) as Config"; if
validation fails, throw a clear error in the catch/validation path instead of
returning the unchecked object. Use a runtime schema validator (e.g., zod) or
explicit property/type checks against the Config interface, and replace the
blind cast with the validated Config instance so downstream consumers receive a
correct shape.

const env = resolved.binDir
? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH}` }
: process.env
const env = resolved.binDir ? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH}` } : process.env
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the file
fd "dbt-resolve.ts" --type f

Repository: AltimateAI/altimate-code

Length of output: 105


🏁 Script executed:

# Read the file around line 210 to see context
cat -n packages/dbt-tools/src/dbt-resolve.ts | sed -n '200,220p'

Repository: AltimateAI/altimate-code

Length of output: 991


🏁 Script executed:

# Check Node.js process.env behavior and verify the concern
# Also check if there are other similar PATH concatenations in the codebase

rg "process\.env\.PATH" packages/dbt-tools/src/ -A 2 -B 2

Repository: AltimateAI/altimate-code

Length of output: 542


🏁 Script executed:

# Check if there are any tests or documentation about environment handling
rg "process\.env" packages/dbt-tools/src/dbt-resolve.ts | head -20

# Also check the type definition of ResolvedDbt to understand the full context
rg "type ResolvedDbt|interface ResolvedDbt" packages/dbt-tools/src/ -A 5

Repository: AltimateAI/altimate-code

Length of output: 1675


🏁 Script executed:

# Check Node.js documentation behavior and verify if process.env.PATH can be undefined
# Also look for how environment is used elsewhere in the codebase

rg "execFileSync|spawnSync" packages/dbt-tools/src/ -B 2 -A 2 | head -30

Repository: AltimateAI/altimate-code

Length of output: 1981


🏁 Script executed:

# Verify Node.js behavior with undefined PATH
# Check if there are any environment setup or tests that might show how PATH is handled

rg "PATH" packages/dbt-tools/src/dbt-resolve.ts -B 1 -A 1

# Also check if there's any documentation or comments about environment handling
rg "binDir" packages/dbt-tools/src/dbt-resolve.ts -B 2 -A 2

Repository: AltimateAI/altimate-code

Length of output: 5142


Guard PATH concatenation when PATH is unset.

Line 210 can produce PATH="<bin>:undefined" if process.env.PATH is missing. The codebase already uses the correct pattern with the nullish coalescing operator in the buildDbtEnv function—apply the same approach here.

🔧 Proposed fix
-    const env = resolved.binDir ? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH}` } : process.env
+    const env = resolved.binDir
+      ? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH ?? ""}` }
+      : process.env
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const env = resolved.binDir ? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH}` } : process.env
const env = resolved.binDir
? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH ?? ""}` }
: process.env
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/dbt-resolve.ts` at line 210, The PATH concatenation on
the env creation line can produce "PATH=<bin>:undefined" when process.env.PATH
is missing; update the construction around resolved.binDir (the const env
assignment that currently uses `${resolved.binDir}:${process.env.PATH}`) to
guard against a missing PATH by using the same nullish coalescing approach as
buildDbtEnv (e.g., use process.env.PATH ?? '' or equivalent) so the PATH becomes
`${resolved.binDir}:${process.env.PATH ?? ''}` when resolved.binDir is present,
otherwise keep process.env unchanged.

mdesmet and others added 2 commits March 25, 2026 18:14
…tibility

Drop ALTIMATE_CODE_PYTHON_PATH and ALTIMATE_CODE_VIRTUAL_ENV — dbt and
Python will be on PATH anyway. Add Windows support: use Scripts/ instead
of bin/, .exe suffix for binaries, where instead of which, skip Unix-only
pyenv/asdf resolution, and add Windows-specific known fallback paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/dbt-tools/src/dbt-resolve.ts (1)

214-214: ⚠️ Potential issue | 🟠 Major

Fix PATH construction in Line 214 for Windows and missing PATH values.

Line 214 still hardcodes : and can produce ...:undefined. This breaks cross-platform intent (Windows requires ;) and repeats the previously flagged issue.

🔧 Proposed fix
-import { dirname, join } from "path"
+import { delimiter, dirname, join } from "path"
@@
-    const env = resolved.binDir ? { ...process.env, PATH: `${resolved.binDir}:${process.env.PATH}` } : process.env
+    const env = resolved.binDir
+      ? { ...process.env, PATH: `${resolved.binDir}${delimiter}${process.env.PATH ?? ""}` }
+      : process.env

Also align buildDbtEnv for consistency (outside this line range):

-    env.PATH = `${resolved.binDir}:${env.PATH ?? ""}`
+    env.PATH = `${resolved.binDir}${delimiter}${env.PATH ?? ""}`
#!/bin/bash
# Verify PATH concatenation sites and delimiter usage in dbt resolver.
rg -n -C2 'PATH:\s*`\$\{[^}]+\}:\$\{[^}]*PATH[^}]*\}`|env\.PATH\s*=\s*`\$\{[^}]+\}:\$\{[^}]*PATH[^}]*\}`' packages/dbt-tools/src/dbt-resolve.ts
rg -n -C2 '\bdelimiter\b' packages/dbt-tools/src/dbt-resolve.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/dbt-tools/src/dbt-resolve.ts` at line 214, The PATH construction
uses a hardcoded ':' and can produce '...:undefined' on missing PATH and on
Windows; update the env creation in the resolved.binDir branch (the const env
assignment referencing resolved.binDir) to join resolved.binDir with the
existing PATH using Node's path.delimiter and default to an empty string or omit
the delimiter when process.env.PATH is falsy, e.g. compute const pathDelimiter =
require('path').delimiter and build PATH as resolved.binDir + (process.env.PATH
? pathDelimiter + process.env.PATH : ''), and mirror the same approach in
buildDbtEnv to keep behavior consistent across both places.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/dbt-tools/test/config.test.ts`:
- Around line 164-193: The tests for discoverPython are flaking when
ALTIMATE_CODE_PYTHON_PATH or ALTIMATE_CODE_VIRTUAL_ENV are set in the host
environment; modify the test setup to save and clear these two env vars before
each test and restore them in afterEach so discoverPython runs in a controlled
environment. Specifically, in the beforeEach/afterEach surrounding the tests
that call discoverPython, capture process.env.ALTIMATE_CODE_PYTHON_PATH and
process.env.ALTIMATE_CODE_VIRTUAL_ENV, delete or set them to undefined before
invoking discoverPython, and restore the original values in afterEach so other
tests are unaffected.

---

Duplicate comments:
In `@packages/dbt-tools/src/dbt-resolve.ts`:
- Line 214: The PATH construction uses a hardcoded ':' and can produce
'...:undefined' on missing PATH and on Windows; update the env creation in the
resolved.binDir branch (the const env assignment referencing resolved.binDir) to
join resolved.binDir with the existing PATH using Node's path.delimiter and
default to an empty string or omit the delimiter when process.env.PATH is falsy,
e.g. compute const pathDelimiter = require('path').delimiter and build PATH as
resolved.binDir + (process.env.PATH ? pathDelimiter + process.env.PATH : ''),
and mirror the same approach in buildDbtEnv to keep behavior consistent across
both places.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c1e588ec-c8cb-406f-ae96-a96b1b8207a6

📥 Commits

Reviewing files that changed from the base of the PR and between fdb8519 and 492b8b8.

📒 Files selected for processing (4)
  • packages/dbt-tools/src/config.ts
  • packages/dbt-tools/src/dbt-resolve.ts
  • packages/dbt-tools/test/config.test.ts
  • packages/dbt-tools/test/dbt-resolve.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/dbt-tools/test/dbt-resolve.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/dbt-tools/src/config.ts

@mdesmet mdesmet changed the title feat(dbt-tools): auto-discover config, expose ALTIMATE_CODE_* env vars for vscode integration feat(dbt-tools): support windows and remove global dbt config file Mar 26, 2026
@anandgupta42 anandgupta42 changed the title feat(dbt-tools): support windows and remove global dbt config file feat(dbt-tools): auto-discover config with Windows compatibility Mar 26, 2026
- Use `path.delimiter` instead of hardcoded `:` in `validateDbt()` and
  `buildDbtEnv()` so PATH injection works on Windows (`;` separator)
- Fix Conda Python discovery on Windows: check env root directly since
  Conda places `python.exe` at `%CONDA_PREFIX%\python.exe`, not in `Scripts/`
- Add `timeout: 5_000` to `execFileSync` in `discoverPython()` to match
  the same pattern used in `dbt-resolve.ts`

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anandgupta42 anandgupta42 merged commit 0e6bb70 into main Mar 26, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants