Skip to content

Latest commit

 

History

History
391 lines (289 loc) · 13.6 KB

File metadata and controls

391 lines (289 loc) · 13.6 KB

CLAUDE.md

Guidance for Claude Code when working with the dtvem codebase.

Related Documentation

Document Purpose
README.md User-facing documentation, installation, usage examples
CONTRIBUTING.md Contribution guidelines, PR process, development setup
CODE_OF_CONDUCT.md Community standards and expected behavior
GO_STYLEGUIDE.md Complete Go coding standards (Google style)

Critical Rules

These rules override all other instructions:

  1. NEVER commit directly to main - Always create a feature branch and submit a pull request. No exceptions.
  2. Follow the styleguide - All code must comply with GO_STYLEGUIDE.md
  3. Write tests - All new/refactored code requires comprehensive unit tests
  4. Cross-platform - All features must work on Windows, macOS, and Linux
  5. Conventional commits - Format: type(scope): description
  6. GitHub Issues for TODOs - Use gh CLI to manage issues, no local TODO files. Use conventional commit format for issue titles
  7. Pull Requests - Use the conventional commit format for PR titles as you do for commits
  8. Run validation before commits - Run ./rnr check (format, lint, test) before committing and pushing
  9. Working an issue - When working an issue, always create a new branch from an updated main branch
  10. Branch Names - Always use the conventional commit type from the issue title as the first prefix, and the scope as the second, then a very short description, example feat/ci/integration-tests
  11. Check branch status before pushing - ALWAYS verify the remote tracking branch still exists before pushing. If a PR was merged/deleted, create a new branch from main instead of trying to push to the old one.
  12. No co-authors - Do not add any co-author information on commits or pull requests
  13. No "generated by" statements - Do not add generated-by statements on pull requests

Quick Reference

Task Runner (rnr)

./rnr --list           # List all available tasks
./rnr check            # Format, lint, and test
./rnr build            # Build both CLI and shim
./rnr test             # Run tests
./rnr format           # Format code
./rnr lint             # Run linter
./rnr clean            # Remove build artifacts
./rnr deploy-local     # Build and deploy to local installation

Build Commands (raw)

# Build executables
go build -o dist/dtvem.exe ./src
go build -o dist/dtvem-shim.exe ./src/cmd/shim

# Run directly
go run ./src/main.go <command>

# Run tests
cd src && go test ./...

Deploy After Building

cp dist/dtvem.exe ~/.dtvem/bin/dtvem.exe
cp dist/dtvem-shim.exe ~/.dtvem/bin/dtvem-shim.exe
~/.dtvem/bin/dtvem.exe reshim

GitHub Issues

gh issue list                    # List open issues
gh issue view <number>           # View details
gh issue create --title "..." --label enhancement --body "..."
gh issue close <number>

GitHub Issue Dependencies (Blocked By / Blocking)

# List what blocks an issue
gh api repos/CodingWithCalvin/dtvem.cli/issues/<number>/dependencies/blocked_by --jq '.[] | "#\(.number) \(.title)"'

# List what an issue blocks
gh api repos/CodingWithCalvin/dtvem.cli/issues/<number>/dependencies/blocking --jq '.[] | "#\(.number) \(.title)"'

# Add a blocking relationship (issue <number> is blocked by <blocker_id>)
# First get the blocker's numeric ID (not issue number):
gh api repos/CodingWithCalvin/dtvem.cli/issues/<blocker_number> --jq '.id'
# Then add the dependency:
gh api repos/CodingWithCalvin/dtvem.cli/issues/<number>/dependencies/blocked_by -X POST -F issue_id=<blocker_id>

# Remove a blocking relationship
gh api repos/CodingWithCalvin/dtvem.cli/issues/<number>/dependencies/blocked_by/<blocker_id> -X DELETE

Note: The API uses numeric issue IDs (not issue numbers) for POST/DELETE operations. Get the ID with gh api repos/CodingWithCalvin/dtvem.cli/issues/<number> --jq '.id'


Project Overview

dtvem (Development Tool Virtual Environment Manager) is a cross-platform runtime version manager written in Go. Similar to asdf but with first-class Windows support.

Attribute Value
Version dev (pre-1.0)
Runtimes Python, Node.js, Ruby
Tests 160+ passing
Style Google Go Style Guide

Key Concept: Shims are Go executables that intercept runtime commands (like python, node), resolve versions, and execute the appropriate binary.

Available Commands

init, install, uninstall, list, list-all, global, local, current, freeze, migrate, reshim, which, where, update, request, version, help


Architecture

Two-Binary System

  1. Main CLI (src/main.go) - The dtvem command
  2. Shim Executable (src/cmd/shim/main.go) - Copied/renamed for each runtime command

Shim Flow

User runs: python --version
    ↓
~/.dtvem/shims/python.exe (shim)
    ↓
Maps to runtime provider → Resolves version
    ↓
├─ Version configured & installed? → Execute ~/.dtvem/versions/python/3.11.0/bin/python
├─ Version configured but not installed? → Show error with install command
└─ No version configured? → Fall back to system PATH or show install instructions

Directory Structure

~/.dtvem/
├── bin/            # dtvem binaries (added to PATH)
│   ├── dtvem
│   └── dtvem-shim
├── shims/          # Runtime shims (python.exe, node.exe, etc.)
├── versions/       # Installed runtimes by name/version
│   ├── python/3.11.0/
│   └── node/18.16.0/
└── config/
    └── runtimes.json  # Global version config

Key Packages

Package Purpose
internal/config/ Paths, version resolution, config file handling
internal/runtime/ Provider interface, registry, version types
internal/shim/ Shim lifecycle management
internal/path/ PATH configuration (platform-specific)
internal/ui/ Colored output, prompts, verbose/debug logging
internal/tui/ Table formatting and styles
internal/download/ File downloads with progress
internal/manifest/ Version manifest fetching and caching
internal/migration/ Migration detection and helpers
internal/testutil/ Shared test utility functions
internal/constants/ Platform constants
src/cmd/ CLI commands (one file per command)
src/runtimes/ Runtime providers (node/, python/, ruby/)

Provider System

All runtimes implement the Provider interface (src/internal/runtime/provider.go, 20 methods total). Providers auto-register via init():

// src/runtimes/node/provider.go
func init() {
    runtime.Register(NewProvider())
}

// src/main.go - blank imports trigger registration
import (
    _ "github.com/CodingWithCalvin/dtvem.cli/src/runtimes/node"
    _ "github.com/CodingWithCalvin/dtvem.cli/src/runtimes/python"
    _ "github.com/CodingWithCalvin/dtvem.cli/src/runtimes/ruby"
)

Key Provider Methods

Method Purpose
Name() Runtime identifier (e.g., "python")
DisplayName() Human-readable name (e.g., "Python")
Shims() Executable names (e.g., ["python", "pip"])
ShouldReshimAfter() Detect global package installs
Install(version) Download and install a version
ExecutablePath(version) Path to versioned executable
GlobalPackages(path) Detect installed global packages
InstallGlobalPackages() Reinstall packages to new version

Adding a New Runtime

  1. Create src/runtimes/<name>/provider.go
  2. Implement runtime.Provider interface (all 20 methods)
  3. Add init() function: runtime.Register(NewProvider())
  4. Import in src/main.go: _ "github.com/CodingWithCalvin/dtvem.cli/src/runtimes/<name>"
  5. Update schemas/runtimes.schema.json enum

The shim mappings are automatically registered via Shims().


Version Resolution

Priority order:

  1. Local: Walk up from pwd looking for .dtvem/runtimes.json (stops at filesystem root)
  2. Global: ~/.dtvem/config/runtimes.json
  3. Error: No version configured

Config format (both local and global):

{
  "python": "3.11.0",
  "node": "18.16.0"
}

Key Features

Automatic Reshim Detection

After npm install -g or pip install, shims prompt to run dtvem reshim to create shims for newly installed executables.

PATH Fallback

When no dtvem version is configured, shims fall back to system PATH (excluding the shims directory).

Migration System

The migrate command detects existing installations (nvm, pyenv, fnm, system) and offers to:

  • Install versions via dtvem
  • Preserve global packages (npm packages, pip packages)
  • Clean up old installations (automated for version managers, manual instructions for system installs)

Note: Configuration file preservation (.npmrc, pip.conf) is not yet implemented.


Coding Standards

All code follows GO_STYLEGUIDE.md. Key points:

  • Naming: Avoid package/receiver repetition, no "Get" prefix
  • Errors: Use structured errors, %w for wrapping
  • Paths: Always use filepath.Join(), never hardcode / or \
  • Output: Use internal/ui package for user-facing messages
  • Tests: Must pass all linters (no special treatment for *_test.go)

Testing

Running Tests

cd src && go test ./...           # All tests
cd src && go test ./internal/config -v  # Specific package
cd src && go test -cover ./...    # With coverage

Test Coverage

  • internal/config/ - Paths, version resolution, directory traversal
  • internal/runtime/ - Registry, provider test harness
  • internal/shim/ - Shim mapping, cache, file operations
  • internal/ui/ - Output formatting functions
  • internal/testutil/ - Test utility functions
  • runtimes/*/ - Provider contract validation
  • cmd/ - Command helpers (migrate, uninstall)

Provider Test Harness

internal/runtime/provider_test_harness.go validates all Provider implementations consistently. Used by node, python, and ruby providers.

Import Cycle Avoidance

  • Runtime providers import internal/shim
  • Tests in internal/shim/ use mock providers (not real ones)
  • Real providers tested via the test harness in their own packages

CI/CD

Workflows in This Repo (.github/workflows/)

Workflow Trigger Purpose
build.yml PR, push to main Lint, build, test on Windows/macOS/Linux. Posts coverage reports on PRs
release.yml Manual dispatch Full release: validate, build 5 platforms, create GitHub Release, notify
commit-lint.yml PR Validate PR titles follow conventional commits
script-lint.yml PR (install scripts) Lint install.sh and install.ps1 with shellcheck/PSScriptAnalyzer
contributors.yml Push to main Auto-update contributors section in README
preview-changelog.yml PR Preview release notes for PRs
integration-test.yml Manual dispatch Full integration test suite (runtimes + migrations)
integration-test-runtimes.yml Manual dispatch Runtime install/uninstall tests only
integration-test-migrations.yml Manual dispatch Migration tests only (all platforms/managers)
generate-manifests-from-r2.yml Manual/scheduled Generate version manifests from R2 mirror
deploy-manifests.yml Push to main (manifests/) Deploy manifest files to R2
mirror-all.yml Manual dispatch Mirror all runtime binaries to R2
mirror-sync.yml Scheduled Sync new versions to R2 mirror

Reusable Workflows (from dtvem/.github repo)

Integration tests and changelog generation use reusable workflows stored in the separate dtvem/.github repository:

Runtime Tests:

  • integration-test-node.yml - Node.js install/global/local/uninstall
  • integration-test-python.yml - Python install/global/local/uninstall
  • integration-test-ruby.yml - Ruby install/global/local/uninstall

Migration Tests (per runtime × platform × version manager):

  • integration-test-migrate-{runtime}-{platform}-{manager}.yml
  • Platforms: ubuntu, macos, windows
  • Managers: system, nvm, fnm, pyenv, rbenv, uru

Utilities:

  • generate-changelog.yml - Generate release notes from commits

Version injected at build time; main branch always shows Version = "dev".


Installation Scripts

One-Command Installers

Unix:

curl -fsSL dtvem.io/install.sh | bash

Windows:

irm dtvem.io/install.ps1 | iex

Features: Auto platform detection, PATH configuration, runs dtvem init.


UI Output System

Use internal/ui for all user-facing output:

Function Purpose
Success() Green ✓ - completed operations
Error() Red ✗ - failures
Warning() Yellow ⚠ - non-critical issues
Info() Cyan → - informational
Progress() Blue → (indented) - operation steps
Header() Bold - section titles
Highlight() Cyan bold - emphasis
HighlightVersion() Magenta bold - version numbers
ActiveVersion() Green bold - active/selected versions
DimText() Gray/faint - secondary info
Debug() / Debugf() Debug output (requires DTVEM_VERBOSE)

Important Notes

  • Cross-platform paths: Use filepath.Join(), check runtime.GOOS
  • Windows shims: Must be .exe files
  • Shim execution: Unix uses syscall.Exec(); Windows uses exec.Command()
  • Version strings: Strip v prefix (e.g., "v22.0.0" → "22.0.0")
  • Registry is global: Providers auto-register on import via init()
  • Verbose mode: Set DTVEM_VERBOSE=1 for debug output