Conversation
- Implement grouped project view with 1-level nesting for organized project categories - Add nested folder support allowing parent directories with sub-projects to display as category headers - Non-selectable category headers show sub-project counts and visual hierarchy - Support for monorepo patterns with projects organized under category folders - New Project struct fields: Depth (nesting level), SubProjectCount, IsGroup, Expanded - Add ViewGroup for navigating into grouped projects - Display child projects indented under parent groups with intuitive UI - Navigation between main view and group view with proper back/forward handling - Comprehensive documentation of grouped project feature in NESTED_PROJECTS.md
…atibility - Implement FeatureConfig interface to allow Config to work with feature system - Stub implementation returns false for all features (ready for future enhancement)
…ject indicators Previously only directories containing project files (.git, package.json, etc.) were displayed in project listings. This caused newly created empty directories to be invisible until they contained project files, creating the appearance of a caching issue. Now all directories are shown in listings, with empty directories marked appropriately (no language, no git status) to distinguish them from actual projects while still being selectable for potential project initialization. Resolves issue where 'when I create a new folder and re-run proj I cant see the folder'
Previously when creating a new project while viewing a group, the shouldReload flag would only refresh the main projects list but not the group projects view. This caused newly created projects within groups to not appear until manually navigating back and forth. Added loadProjectsAndRefreshGroup() method that: - Reloads the main projects list - Refreshes group projects if currently viewing a group - Updates both project list and group list models - Ensures UI consistency after project creation within groups
Fixed fundamental issue where groups only showed directories containing project indicators (.git, package.json, etc.), causing most directories to be invisible. Scanner changes: - findChildProjects now shows ALL directories, not just project roots - Empty directories are marked appropriately (no language, no git status) - Consistent behavior with top-level directory scanning New features: - Added F5 and 'r' key bindings to refresh/rescan projects - Works in both main project view and group view - Updated help text to show refresh shortcuts This fixes the issue where gamedev showed only 2 of 6 directories.
Clarified which display options are actually implemented vs planned: Working options: - showHiddenDirs: ✅ implemented - sortBy: ✅ implemented Documented but not yet implemented: - showGitStatus: defined in config but not used (always shows) - showLanguage: defined in config but not used (always shows) Not in config struct (planned only): - showNestedProjects: removed from example, noted as always-on - maxScanDepth: removed from example, noted as always 1 Updated NESTED_PROJECTS.md with notes about current hardcoded behavior vs planned configurable options.
Removed showGitStatus and showLanguage from: - DisplayConfig struct - DefaultConfig() - viper defaults - CONFIG.md documentation These options were defined but never wired up to the display code. Git status and language are always shown - no config needed. Also cleaned up NESTED_PROJECTS.md to remove misleading 'planned' configuration options that don't exist in the config struct. Display config now only contains working options: - showHiddenDirs: controls hidden directory visibility - sortBy: controls project sort order
Added comprehensive shell integration for multiple shells: New shell-integration.sh script: - Auto-detects shell type (bash, zsh, fish, nushell, elvish, powershell) - Automatically adds integration function to appropriate config file - Handles edge cases like existing functions and directory creation Enhanced install.sh: - Added fish shell support with proper syntax - Improved shell detection and setup flow - Better error handling for unsupported shells Updated documentation: - README.md: Added fish shell integration example - INSTALL.md: Added 6 shell examples plus auto-setup instructions - Covers bash, zsh, fish, nushell, elvish, powershell This addresses the issue where fish users had to manually figure out the correct function syntax and provides broader shell ecosystem support.
Removed all shell integration setup functionality: - Deleted scripts/shell-integration.sh (173 lines) - Removed shell integration functions from scripts/install.sh (149 lines) - Removed shell integration documentation from docs/INSTALL.md (115 lines) - Removed shell integration examples from README.md (32 lines) Total reduction: 468 lines This simplifies the project by removing complex multi-shell integration setup that was adding maintenance overhead.
Shell Integration Features: - Created shell-specific integration files (bash, zsh, fish) in scripts/shells/ - Enhanced install.sh to detect current shell and download appropriate integration - Added graceful handling for unsupported shells with helpful error messages - Installer warns but doesn't block when shell is unsupported Documentation Updates: - Added comprehensive shell integration section to docs/INSTALL.md - Enhanced docs/CONTRIBUTING.md with detailed guide for adding new shell support - Updated README.md with shell integration overview and supported shells list - Included links to contribution guide for extending shell support Files Added: - scripts/shells/bash.sh - Bash integration with wrapper function - scripts/shells/zsh.sh - Zsh integration with wrapper function - scripts/shells/fish.fish - Fish integration with wrapper function Enhanced User Experience: - Auto-detection of current shell during installation - Automatic download and setup of shell integration files - Clear instructions for manual setup when auto-setup fails - Helpful links to documentation and contribution guides for unsupported shells
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
There was a problem hiding this comment.
Pull request overview
This pull request adds comprehensive monorepo and nested project support with grouped navigation, alongside an enhanced shell integration system that supports bash, zsh, and fish shells. The changes enable users to work with project hierarchies (1-level nesting) and provide better shell integration with per-shell scripts and automatic detection.
Changes:
- New grouped scanning functionality to detect and display nested projects (groups, monorepos)
- Enhanced shell integration with dedicated scripts for bash, zsh, and fish shells
- Improved UX with group navigation, refresh functionality, and visual indicators for groups/monorepos
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/shells/bash.sh | New bash shell integration script with wrapper function for directory changing |
| scripts/shells/zsh.sh | New zsh shell integration script with wrapper function for directory changing |
| scripts/shells/fish.fish | New fish shell integration script with wrapper function for directory changing |
| scripts/install.sh | Enhanced installer with shell auto-detection and integration setup |
| internal/project/scanner.go | Major refactoring to support nested/grouped project scanning with 1-level depth |
| internal/project/scanner_test.go | Minor test updates to add project markers for existing tests |
| internal/tui/views/project_list.go | Enhanced rendering to show groups, monorepos, and nested projects with icons |
| internal/tui/keys.go | Added refresh key binding (F5/r) |
| internal/app/app.go | New ViewGroup state with navigation, group-aware project creation, and refresh |
| internal/config/config.go | Removed unused display config options, added stub feature flag function |
| go.mod | Moved cobra from indirect to direct dependency |
| docs/INSTALL.md | Updated with comprehensive shell integration documentation |
| docs/CONTRIBUTING.md | Added detailed guide for contributing new shell support |
| docs/CONFIG.md | Removed documentation for deleted config options |
| README.md | Updated with shell integration overview and supported shells |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Calculate padding for alignment | ||
| visibleLen := len(prefix) + len(p.Name) | ||
| if hasChildren { | ||
| visibleLen += len(fmt.Sprintf(" (%d)", p.SubProjectCount)) | ||
| } | ||
| if isGroup || hasChildren { | ||
| visibleLen += 3 // icon | ||
| } | ||
| padding := 35 - visibleLen |
There was a problem hiding this comment.
The padding calculation uses len() on strings that may contain emoji characters (like "📁" and "📦"). In Go, len() returns the byte count, not the visual character count. Emoji characters typically take 3-4 bytes but display as 1 character width. This will cause misalignment in the rendered output. Consider using a library that properly counts grapheme clusters or visible width (like github.com/mattn/go-runewidth).
| if len(actions) > insertIdx { | ||
| actions = append(actions[:insertIdx+1], append([]views.Action{childAction}, actions[insertIdx+1:]...)...) |
There was a problem hiding this comment.
This slice manipulation uses a complex nested append pattern that is difficult to read and error-prone. Consider using a clearer approach: create a new slice with explicit elements, or use a temporary slice to build the action list before combining.
| } else { | ||
| // It's a regular directory (not a project yet, but still show it) | ||
| // This allows users to see directories they create and potentially initialize as projects | ||
| proj, err := s.scanProject(entry.Name(), dirPath, 0) | ||
| if err != nil { | ||
| continue | ||
| } | ||
| // Mark as not a real project yet (no language, no git status) | ||
| proj.Language = "" | ||
| proj.IsGitRepo = false | ||
| proj.GitBranch = "" | ||
| proj.GitDirty = false | ||
| projects = append(projects, proj) | ||
| } |
There was a problem hiding this comment.
The scanner now shows ALL directories regardless of whether they are projects (lines 123-136). This significantly changes the UX by cluttering the project list with empty directories. Combined with the overly broad isProjectRoot check (which includes README files), this could show many unrelated directories. Consider adding a configuration option to control this behavior or being more selective about which empty directories to show.
| // scanWithGroups scans top level and detects groups vs projects | ||
| func (s *Scanner) scanWithGroups(basePath string) ([]*Project, error) { | ||
| entries, err := os.ReadDir(basePath) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| projects := make([]*Project, 0) | ||
|
|
||
| for _, entry := range entries { | ||
| // Skip files, only process directories | ||
| if !entry.IsDir() { | ||
| continue | ||
| } | ||
|
|
||
| // Skip hidden directories unless enabled | ||
| if !s.showHidden && strings.HasPrefix(entry.Name(), ".") { | ||
| continue | ||
| } | ||
|
|
||
| // Skip excluded patterns | ||
| if s.isExcluded(entry.Name()) { | ||
| continue | ||
| } | ||
|
|
||
| projectPath := filepath.Join(expandedPath, entry.Name()) | ||
| dirPath := filepath.Join(basePath, entry.Name()) | ||
| isProject := isProjectRoot(dirPath) | ||
|
|
||
| // Check if this directory contains projects (making it a group) | ||
| childProjects := s.findChildProjects(dirPath) | ||
|
|
||
| if isProject { | ||
| // It's a project (and possibly also contains sub-projects - monorepo) | ||
| proj, err := s.scanProject(entry.Name(), dirPath, 0) | ||
| if err != nil { | ||
| continue | ||
| } | ||
| proj.SubProjectCount = len(childProjects) | ||
| proj.Expanded = true // Projects with children are expanded by default | ||
| projects = append(projects, proj) | ||
|
|
||
| // Add child projects | ||
| for _, child := range childProjects { | ||
| child.ParentPath = dirPath | ||
| child.Depth = 1 | ||
| projects = append(projects, child) | ||
| } | ||
| } else if len(childProjects) > 0 { | ||
| // It's a group folder (not a project itself, but contains projects) | ||
| group := &Project{ | ||
| Name: entry.Name(), | ||
| Path: dirPath, | ||
| Depth: 0, | ||
| IsGroup: true, | ||
| SubProjectCount: len(childProjects), | ||
| Expanded: true, | ||
| } | ||
|
|
||
| // Get last modified from directory | ||
| info, _ := os.Stat(dirPath) | ||
| if info != nil { | ||
| group.LastModified = info.ModTime() | ||
| } | ||
|
|
||
| projects = append(projects, group) | ||
|
|
||
| // Add child projects | ||
| for _, child := range childProjects { | ||
| child.ParentPath = dirPath | ||
| child.Depth = 1 | ||
| projects = append(projects, child) | ||
| } | ||
| } else { | ||
| // It's a regular directory (not a project yet, but still show it) | ||
| // This allows users to see directories they create and potentially initialize as projects | ||
| proj, err := s.scanProject(entry.Name(), dirPath, 0) | ||
| if err != nil { | ||
| continue | ||
| } | ||
| // Mark as not a real project yet (no language, no git status) | ||
| proj.Language = "" | ||
| proj.IsGitRepo = false | ||
| proj.GitBranch = "" | ||
| proj.GitDirty = false | ||
| projects = append(projects, proj) | ||
| } | ||
| // No longer skip any directories - show all | ||
| } | ||
|
|
||
| return projects, nil | ||
| } |
There was a problem hiding this comment.
The new grouped/nested project scanning functionality (including IsGroup, SubProjectCount, Depth, ParentPath fields and the scanWithGroups/findChildProjects methods) lacks test coverage. The existing tests in scanner_test.go were only minimally updated to add project markers but don't test the new nested/group behavior. Add comprehensive tests for group detection, monorepo scenarios, parent-child relationships, and the depth-based filtering.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
🚀 Monorepo Support, subfolders & enhanced shell integration
Overview
This PR introduces comprehensive monorepo/nested project support with grouped navigation and enhanced shell integration system for the proj TUI project navigator. The implementation includes significant scanner improvements, UX enhancements, and a streamlined shell integration approach.
(I should probably have broken this PR down a bit)
🎯 Key Features
🏗️ Monorepo & Nested Project Support
🐚 Shell Integration System
📋 Changes Made
Core Scanner Enhancements
scanWithGroups()method for nested project detectionfindChildProjects()to display all directories, not just project rootsisProjectRoot()logic with more project indicatorsIsGroupandSubProjectCountfields to Project structApplication Logic Updates
ViewGroupstate with dedicated group list viewloadProjectsAndRefreshGroup()for context-aware refreshingShell Integration Improvements
zsh.sh, andfish.fishDocumentation & Contribution Guide
🎨 User Experience Improvements
Monorepo Navigation
Shell Integration UX
📖 Documentation Updates
Enhanced Installation Guide (INSTALL.md)
Detailed Contributing Guide (CONTRIBUTING.md)
Updated README (README.md)
🧪 Testing & Quality
Enhanced Test Coverage
Code Quality
🔄 Migration & Backward Compatibility
Seamless Upgrade
Shell Integration Migration