Skip to content

Commit 79b9cbd

Browse files
Use glob patterns
1 parent bf892e8 commit 79b9cbd

File tree

1 file changed

+75
-46
lines changed

1 file changed

+75
-46
lines changed

main.go

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import (
1515
var ignore gitignore.GitIgnore
1616

1717
type Config struct {
18-
// one of available encodings from https://github.com/pkoukk/tiktoken-go?tab=readme-ov-file#available-encodings
19-
Model string `flag:"model" envvar:"MODEL" default:"gpt-4"` // "The model to use for tokenization."
20-
OutputFile string `flag:"output" envvar:"FILE" default:"file-for-ai.txt"`
18+
Model string `flag:"model" envvar:"MODEL" default:"gpt-4"`
19+
OutputFile string `flag:"output" envvar:"FILE" default:"file-for-ai.txt"`
20+
IgnoreGitIgnore bool `flag:"ignore-gitignore" envvar:"IGNORE_GITIGNORE" default:"false"`
2121
}
2222

2323
func main() {
@@ -28,30 +28,21 @@ func main() {
2828
panic(err)
2929
}
3030

31-
// Check if at least a directory path is provided
3231
if len(os.Args) < 2 {
33-
fmt.Println("Error: Directory path is required.")
34-
fmt.Println("Usage: file-for-ai <directory> [output file]")
32+
fmt.Println("Error: Directory path or glob pattern is required.")
33+
fmt.Println("Usage: file-for-ai <directory|pattern> [output file]")
3534
os.Exit(1)
3635
}
3736

38-
directoryPath := os.Args[1]
37+
inputPath := os.Args[1]
3938

4039
outputFileName := conf.OutputFile
4140

42-
// Backup the output file if it already exists
4341
if _, err := os.Stat(outputFileName); !os.IsNotExist(err) {
4442
fmt.Printf("Output file %s already exists\n", outputFileName)
4543
os.Exit(1)
4644
}
4745

48-
//gitIgnorePath := filepath.Join(directoryPath, ".gitignore")
49-
ignore, err = gitignore.NewRepository(directoryPath)
50-
if err != nil {
51-
fmt.Println("Error creating output file:", err)
52-
os.Exit(1)
53-
}
54-
5546
outputFile, err := os.Create(outputFileName)
5647
if err != nil {
5748
fmt.Println("Error creating output file:", err)
@@ -69,57 +60,96 @@ func main() {
6960
tokens := 0
7061

7162
fmt.Println("Merging files:")
72-
err = filepath.Walk(directoryPath, func(path string, info os.FileInfo, err error) error {
73-
if info.Name() == outputFileName {
74-
return nil
75-
}
7663

77-
if err != nil {
78-
fmt.Println("Error accessing path:", path, err)
79-
return err
80-
}
64+
if isDirectory(inputPath) {
8165

82-
relativePath, err := filepath.Rel(directoryPath, path)
83-
if err != nil {
84-
fmt.Println("Error processing relative path:", path, err)
85-
return err
66+
if !conf.IgnoreGitIgnore {
67+
fmt.Print("Filtering files using .gitignore... ")
68+
ignore, err = gitignore.NewRepository(inputPath)
69+
if err != nil {
70+
fmt.Println("Error creating gitignore repository:", err)
71+
os.Exit(1)
72+
}
8673
}
8774

88-
if !info.IsDir() && !isGitIgnored(relativePath, info.IsDir()) && isTextFile(path) && !strings.HasPrefix(path, ".") {
89-
fileContents, err := os.ReadFile(path)
75+
err = filepath.Walk(inputPath, func(path string, info os.FileInfo, err error) error {
9076
if err != nil {
91-
fmt.Println("Error reading file:", path, err)
77+
fmt.Println("Error accessing path:", path, err)
9278
return err
9379
}
94-
fmt.Println(relativePath)
95-
tokens += len(tkm.Encode(string(fileContents), nil, nil))
80+
return processFile(inputPath, path, info, outputFile, tkm, &tokens, outputFileName)
81+
})
82+
} else {
83+
files, err := filepath.Glob(inputPath)
84+
if err != nil {
85+
fmt.Println("Error parsing glob pattern:", err)
86+
os.Exit(1)
87+
}
9688

97-
separator := fmt.Sprintf("\n\n>>>>>> %s <<<<<<\n\n", relativePath)
98-
if _, err := outputFile.WriteString(separator); err != nil {
99-
fmt.Println("Error writing separator to output file:", err)
100-
return err
89+
for _, path := range files {
90+
info, err := os.Stat(path)
91+
if err != nil {
92+
fmt.Println("Error accessing file:", path, err)
93+
continue
10194
}
102-
103-
if _, err := outputFile.Write(fileContents); err != nil {
104-
fmt.Println("Error writing file contents to output file:", err)
105-
return err
95+
err = processFile(filepath.Dir(path), path, info, outputFile, tkm, &tokens, outputFileName)
96+
if err != nil {
97+
fmt.Println("Error processing file:", path, err)
98+
continue
10699
}
107100
}
108-
109-
return nil
110-
})
101+
}
111102

112103
fmt.Println()
113104
if err != nil {
114-
fmt.Println("Error walking through the directory:", err)
105+
fmt.Println("Error walking through the directory or processing pattern:", err)
115106
return
116107
}
117108

118109
fmt.Printf("Files merged successfully into %s\n", outputFileName)
119110
fmt.Printf("Total tokens for model %s: %s\n", conf.Model, formatIntNumber(tokens))
120111
}
121112

122-
// formats int by adding spaces between thousands
113+
func isDirectory(path string) bool {
114+
info, err := os.Stat(path)
115+
return err == nil && info.IsDir()
116+
}
117+
118+
func processFile(basePath, path string, info os.FileInfo, outputFile *os.File, tkm *tiktoken.Tiktoken, tokens *int, outputFileName string) error {
119+
if info.Name() == outputFileName {
120+
return nil
121+
}
122+
123+
relativePath, err := filepath.Rel(basePath, path)
124+
if err != nil {
125+
fmt.Println("Error processing relative path:", path, err)
126+
return err
127+
}
128+
129+
if !info.IsDir() && !isGitIgnored(relativePath, info.IsDir()) && isTextFile(path) && !strings.HasPrefix(path, ".") {
130+
fileContents, err := os.ReadFile(path)
131+
if err != nil {
132+
fmt.Println("Error reading file:", path, err)
133+
return err
134+
}
135+
fmt.Println(relativePath)
136+
*tokens += len(tkm.Encode(string(fileContents), nil, nil))
137+
138+
separator := fmt.Sprintf("\n\n>>>>>> %s <<<<<<\n\n", relativePath)
139+
if _, err := outputFile.WriteString(separator); err != nil {
140+
fmt.Println("Error writing separator to output file:", err)
141+
return err
142+
}
143+
144+
if _, err := outputFile.Write(fileContents); err != nil {
145+
fmt.Println("Error writing file contents to output file:", err)
146+
return err
147+
}
148+
}
149+
150+
return nil
151+
}
152+
123153
func formatIntNumber(n int) string {
124154
s := fmt.Sprintf("%d", n)
125155
if len(s) <= 3 {
@@ -148,7 +178,6 @@ func isGitIgnored(path string, isDir bool) bool {
148178
return false
149179
}
150180

151-
// isTextFile checks the file extension against a list of known non-text file extensions.
152181
func isTextFile(path string) bool {
153182
ext := strings.ToLower(filepath.Ext(path))
154183
return !nonTextFileExtensions[ext]

0 commit comments

Comments
 (0)