Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 38 additions & 13 deletions .agentreerc.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
#!/usr/bin/env bash
# Example .agentreerc - Project-specific agentree configuration
# Copy this to .agentreerc in your project root and customize
# agentree Configuration Example
# This file demonstrates the new environment configuration features

# Post-create scripts to run after worktree creation
# These take precedence over auto-detection
POST_CREATE_SCRIPTS=(
"pnpm install"
"pnpm build"
"pnpm test"
# Environment Configuration
# ========================

# Enable/disable environment file copying (default: true)
ENV_COPY_ENABLED=true

# Enable recursive search for env files in monorepos (default: true)
ENV_RECURSIVE=true

# Use .gitignore patterns as source of truth (default: true)
ENV_USE_GITIGNORE=true

# Additional patterns to include beyond .gitignore
# These patterns will be searched for and copied even if not in .gitignore
ENV_INCLUDE_PATTERNS=(
"*.env.example"
"config/*.sample"
"secrets/*.template"
".vscode/settings.local.json"
".idea/workspace.local.xml"
)

# Patterns to exclude even if found in .gitignore
# Useful for excluding test or temporary env files
ENV_EXCLUDE_PATTERNS=(
"*.test.env"
"*.temp.env"
"node_modules/**/.env"
"dist/**/.env"
)

# You can also customize based on conditions
# if [[ -f ".env.example" ]]; then
# POST_CREATE_SCRIPTS+=("cp .env.example .env")
# fi
# Post-create scripts
# These run after worktree creation and env file copying
POST_CREATE_SCRIPTS=(
"npm install"
"npm run build"
"echo 'Worktree ready for AI agent!'"
)
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ agentree -b feature-x
That's it! agentree will:
- Create a new branch prefixed with `agent/` (e.g., `agent/feature-x`)
- Set up an isolated worktree in `../myrepo-worktrees/`
- Copy your `.env` and `.dev.vars` files
- Intelligently discover and copy environment files based on `.gitignore`
- Copy AI tool configs (`.claude/settings.local.json`, `.cursorrules`, etc.)
- Run setup commands (auto-detected: npm/pnpm/yarn install, etc.)
- Switch you to the new worktree directory

Expand Down
63 changes: 58 additions & 5 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
runSetup bool
interactive bool
customScripts []string
verbose bool
)

// createCmd represents the create command
Expand All @@ -53,6 +54,7 @@ func init() {
createCmd.Flags().BoolVarP(&runSetup, "setup", "s", true, "Run setup scripts (auto-detect or from config)")
createCmd.Flags().BoolVarP(&interactive, "interactive", "i", false, "Interactive wizard to guide through setup")
createCmd.Flags().StringArrayVarP(&customScripts, "script", "S", nil, "Custom post-create script (can be used multiple times)")
createCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Show detailed environment discovery process")

// Make branch required unless in interactive mode
_ = createCmd.MarkFlagRequired("branch")
Expand All @@ -70,6 +72,7 @@ func init() {
rootCmd.Flags().BoolVarP(&runSetup, "setup", "s", true, "Run setup scripts")
rootCmd.Flags().BoolVarP(&interactive, "interactive", "i", false, "Interactive wizard")
rootCmd.Flags().StringArrayVarP(&customScripts, "script", "S", nil, "Custom post-create script")
rootCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Show detailed discovery process")

// If root command is called with flags, run create
rootCmd.RunE = func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -186,12 +189,62 @@ func runCreate(cmd *cobra.Command, args []string) error {

// Copy environment files if requested
if copyEnv {
copiedFiles, err := env.CopyEnvFiles(repo.Root, dest)
if err != nil {
fmt.Fprintln(os.Stderr, errorStyle.Render(fmt.Sprintf("Warning: %v", err)))
// Load configurations and merge them
projectConfig, _ := config.LoadProjectConfig(repo.Root)
globalConfig, _ := config.LoadGlobalConfig()
mergedConfig := config.MergeConfig(globalConfig, projectConfig)

// Check if env copying is enabled in config
if !mergedConfig.EnvConfig.Enabled {
fmt.Println(infoStyle.Render("Environment file copying disabled by configuration"))
} else {
for _, file := range copiedFiles {
fmt.Printf("πŸ“‹ Copied %s\n", file)
// Use the enhanced copier with configuration
copier := env.NewEnvFileCopier(repo.Root, dest)
copier.SetVerbose(verbose)

// Add custom patterns from config
if len(mergedConfig.EnvConfig.IncludePatterns) > 0 {
copier.AddCustomPatterns(mergedConfig.EnvConfig.IncludePatterns)
}

// Discover files based on .gitignore and patterns
fmt.Println(infoStyle.Render("Discovering environment files..."))
files, err := copier.DiscoverFiles()
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: Error discovering files: %v\n", err)
files = []string{} // Continue with empty list instead of failing
}

if len(files) > 0 {
// Filter out excluded patterns
var filteredFiles []string
for _, file := range files {
excluded := false
for _, pattern := range mergedConfig.EnvConfig.ExcludePatterns {
if matched, _ := filepath.Match(pattern, file); matched {
excluded = true
break
}
}
if !excluded {
filteredFiles = append(filteredFiles, file)
}
}

// Copy the filtered files
if len(filteredFiles) > 0 {
copiedFiles, err := copier.CopyFiles(filteredFiles)
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: Some files couldn't be copied: %v\n", err)
}
for _, file := range copiedFiles {
fmt.Printf("πŸ“‹ Copied %s\n", file)
}
} else {
fmt.Println(infoStyle.Render("No environment files found to copy"))
}
} else {
fmt.Println(infoStyle.Render("No environment files found to copy"))
}
}
}
Expand Down
67 changes: 67 additions & 0 deletions docs/environment-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Environment Configuration

agentree intelligently discovers and copies environment files based on `.gitignore` patterns, with support for monorepos and AI tool configurations.

## How It Works

1. Parses all `.gitignore` files in your repository
2. Identifies environment-related patterns (`.env`, `local`, `secret`, etc.)
3. Finds matching files and copies them to the new worktree
4. Includes AI tool configs (`.claude/settings.local.json`, `.cursorrules`, etc.)

## Configuration

### Project Config (.agentreerc)

```bash
# Additional patterns to include
ENV_INCLUDE_PATTERNS=(
"*.env.example"
"config/*.sample"
)

# Patterns to exclude
ENV_EXCLUDE_PATTERNS=(
"*.test.env"
"node_modules/**/.env"
)
```

### Global Config (~/.config/agentree/config)

```bash
# Comma-separated patterns
ENV_INCLUDE_PATTERNS=.env.global,.company-secrets
ENV_EXCLUDE_PATTERNS=*.backup,*.tmp
```

## Examples

```bash
# Basic usage - auto-discovers from .gitignore
agentree create -b feature/new-api

# Disable environment copying
agentree create -b feature/test -e=false

# Debug discovery process
agentree create -b feature/debug -v
```

## Supported Files

- **Auto-detected**: Files in `.gitignore` containing keywords like `.env`, `local`, `secret`
- **AI configs**: `.claude/settings.local.json`, `.cursorrules`, `.github/copilot/config.json`
- **Monorepo**: Recursive patterns like `**/.env`, `packages/*/.env`

## Troubleshooting

- **No files copied?** Check if files exist and are in `.gitignore`
- **Too many files?** Add exclude patterns to `.agentreerc`
- **Debug mode**: Use `-v` flag to see discovery details

## Best Practices

1. Add environment files to `.gitignore` for automatic discovery
2. Use `.env.local` for machine-specific settings
3. Keep AI configs like `.claude/settings.local.json` for project context
Loading
Loading