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
22 changes: 22 additions & 0 deletions cmd/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cmd

import (
"github.com/RohitRavindra-dev/devlocal/internal/filesystem"
"github.com/RohitRavindra-dev/devlocal/internal/service/status"
"github.com/spf13/cobra"
)

var statusCmd = &cobra.Command{
Use: "status",
Short: "View the current status of devlocal setup",
PreRunE: func(cmd *cobra.Command, args []string) error {
return filesystem.ValidateDevLocalFilesystem()
},
RunE: func(cmd *cobra.Command, args []string) error {
return status.Run()
},
}

func init() {
rootCmd.AddCommand(statusCmd)
}
19 changes: 12 additions & 7 deletions internal/filesystem/utils.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
package filesystem

import (
"fmt"
"os"
"path/filepath"

"github.com/RohitRavindra-dev/devlocal/internal/config"
"gopkg.in/yaml.v3"
)

func exists(path string) (bool, error) {
_, err := os.Stat(path)
func FileExists(path string) (bool, error) {
info, err := os.Stat(path)

if err == nil {
return true, nil
if os.IsNotExist(err) {
return false, fmt.Errorf("%s does not exist", path)
}

if os.IsNotExist(err) {
return false, nil
if err != nil {
return false, err
}

if info.IsDir() {
return false, fmt.Errorf("%s is a directory, expected a file", path)
}

return false, err
return true, nil
}

func LoadDevlocalConfig() (*config.DevlocalConfigYaml, error) {
Expand Down
15 changes: 15 additions & 0 deletions internal/git/worktrees.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,18 @@ func sanitizeToTrackedFiles(files []string) []string {
return filesTracked

}

func IsSkipWorktree(file string) (bool, error) {
out, err := exec.Command(
"git",
"ls-files",
"-v",
file,
).CombinedOutput()

if err != nil {
return false, err
}

return len(out) > 0 && out[0] == 'S', nil
Comment on lines +78 to +90

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -u
set +e

echo '$ git ls-files -v __coderabbit_unmatched_probe__'
git ls-files -v __coderabbit_unmatched_probe__
echo "exit=$?"

echo
echo '$ git ls-files -v --error-unmatch -- __coderabbit_unmatched_probe__'
git ls-files -v --error-unmatch -- __coderabbit_unmatched_probe__
echo "exit=$?"

Repository: RohitRavindra-dev/devlocal

Length of output: 413


🏁 Script executed:

#!/bin/bash
set -u

echo '### internal/git/worktrees.go'
sed -n '1,180p' internal/git/worktrees.go

echo
echo '### internal/service/status/reporter.go'
sed -n '1,220p' internal/service/status/reporter.go

Repository: RohitRavindra-dev/devlocal

Length of output: 3574


🏁 Script executed:

#!/bin/bash
set -u

rg -n "IsSkipWorktree\(" .

Repository: RohitRavindra-dev/devlocal

Length of output: 355


🏁 Script executed:

#!/bin/bash
set -u

rg -n "IsSkipWorktree\(" .

Repository: RohitRavindra-dev/devlocal

Length of output: 355


Make IsSkipWorktree failures propagate to status output. git ls-files -v can return no output for an unmatched path, and displayOverlookStatus currently discards the error from git.IsSkipWorktree(filename), so non-tracked paths still render as “Being tracked”. Make unmatched paths return an error in internal/git/worktrees.go and handle that error in internal/service/status/reporter.go instead of ignoring it.

🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 79-79: os/exec.Command must not be called. use os/exec.CommandContext

(noctx)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/git/worktrees.go` around lines 78 - 90, `IsSkipWorktree` currently
treats an empty `git ls-files -v` result as success, so unmatched paths are
misreported downstream. Update `IsSkipWorktree` in the worktrees helper to
return an error when `CombinedOutput` yields no output for the requested file,
and then change `displayOverlookStatus` in the status reporter to handle the
returned error from `git.IsSkipWorktree(filename)` instead of discarding it so
non-tracked paths do not show as “Being tracked”.

}
76 changes: 76 additions & 0 deletions internal/service/status/reporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package status

import (
"fmt"

"github.com/RohitRavindra-dev/devlocal/internal/config"
"github.com/RohitRavindra-dev/devlocal/internal/filesystem"
"github.com/RohitRavindra-dev/devlocal/internal/git"
)

func displayConfigVersion(version int) {
fmt.Printf("[Version] %d \n", version)
}

func displayOverlookStatus(fileNames []string) {
fmt.Println("[Overlooked files]")
if len(fileNames) == 0 {
fmt.Println("\t- No files registed in config for overlooking!")
return
}
for _, filename := range fileNames {
exists, err := filesystem.FileExists(filename)
overlookStatus := "✗"
overlookComment := "Being tracked"
if exists {

if isOverlooked, _ := git.IsSkipWorktree(filename); isOverlooked {
overlookStatus = "✓"
overlookComment = "Overlooked"
Comment on lines +27 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't swallow IsSkipWorktree failures.

When git.IsSkipWorktree errors, the blank identifier leaves the default "Being tracked" message in place. That turns git/read failures into a false healthy status instead of surfacing that the inspection failed.

Suggested fix
-			if isOverlooked, _ := git.IsSkipWorktree(filename); isOverlooked {
+			isOverlooked, err := git.IsSkipWorktree(filename)
+			if err != nil {
+				overlookComment = err.Error()
+			} else if isOverlooked {
 				overlookStatus = "✓"
 				overlookComment = "Overlooked"
 			}
📝 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
if isOverlooked, _ := git.IsSkipWorktree(filename); isOverlooked {
overlookStatus = "✓"
overlookComment = "Overlooked"
isOverlooked, err := git.IsSkipWorktree(filename)
if err != nil {
overlookComment = err.Error()
} else if isOverlooked {
overlookStatus = "✓"
overlookComment = "Overlooked"
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/status/reporter.go` around lines 27 - 29, The status
reporter is ignoring errors from git.IsSkipWorktree, which can leave the default
“Being tracked” state even when the check failed. Update the logic in the
reporter’s skip-worktree check to handle the returned error explicitly instead
of using the blank identifier; if git.IsSkipWorktree fails, surface that failure
in the reported status/comment rather than treating the file as healthy, and
keep the normal overlookStatus/overlookComment assignment only when the check
succeeds.

}

} else {
overlookComment = err.Error()
}

fmt.Printf("\t%s %s \n\t\t- %s\n", overlookStatus, filename, overlookComment)

}
}

func displayPatchesStatus(patches []string) {
fmt.Println("[Patches]")
if len(patches) == 0 {
fmt.Println("\t- No patches registed in config for patching!")
return
}
}
Comment on lines +41 to +47

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Configured patches never render any status.

For any non-empty patches, this function prints [Patches] and exits without listing a single entry. The new status command therefore omits patch state exactly when patch configuration exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/status/reporter.go` around lines 41 - 47, The
displayPatchesStatus function currently prints the section header and returns
without rendering anything when patches is non-empty, so add the missing
iteration over the patches slice inside displayPatchesStatus to print each
configured patch’s status before returning. Keep the empty-slice branch for the
“No patches registed…” message, and use the existing displayPatchesStatus symbol
so the status command shows patch entries whenever configuration exists.


func displayDevlocalConfig(config *config.DevlocalConfigYaml) error {
fmt.Println("\n===============================")
fmt.Println("| devlocal [Status] |")
fmt.Printf("===============================\n\n")
displayConfigVersion(config.Version)
fmt.Println()
displayOverlookStatus(config.Overlook)
fmt.Println()
displayPatchesStatus(config.Patches)
fmt.Printf("===============================\n\n")

return nil
}

func Run() error {

//load config
config, err := filesystem.LoadDevlocalConfig()
if err != nil {
return err
}

if err := displayDevlocalConfig(config); err != nil {
return err
}

return nil
}
Loading