feat: add prompt strategies and improve installation documentation

## CHANGES

- Add prompt strategies like Chain of Thought (CoT)
- Implement strategy selection with `--strategy` flag
- Improve README with platform-specific installation instructions
- Fix web interface documentation link
- Refactor git operations with new githelper package
- Add `--liststrategies` command to view available strategies
- Support applying strategies to system prompts
- Fix YouTube configuration check
- Improve error handling in session management
This commit is contained in:
Kayvan Sylvan
2025-03-15 00:30:30 -07:00
parent 29f4534001
commit 096f40df68
18 changed files with 515 additions and 205 deletions

View File

@@ -2,19 +2,13 @@ package tools
import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"github.com/danielmiessler/fabric/plugins"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/danielmiessler/fabric/plugins/tools/githelper"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/otiai10/copy"
)
@@ -89,7 +83,7 @@ func (o *PatternsLoader) Setup() (err error) {
// PopulateDB downloads patterns from the internet and populates the patterns folder
func (o *PatternsLoader) PopulateDB() (err error) {
fmt.Printf("Downloading patterns and Populating %s..\n", o.Patterns.Dir)
fmt.Printf("Downloading patterns and Populating %s...\n", o.Patterns.Dir)
fmt.Println()
if err = o.gitCloneAndCopy(); err != nil {
return
@@ -148,156 +142,20 @@ func (o *PatternsLoader) movePatterns() (err error) {
}
func (o *PatternsLoader) gitCloneAndCopy() (err error) {
// Clones the given repository, creating the remote, the local branches
// and fetching the objects, everything in memory:
var r *git.Repository
if r, err = git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
URL: o.DefaultGitRepoUrl.Value,
}); err != nil {
fmt.Println(err)
return
// Create temp folder if it doesn't exist
if err = os.MkdirAll(filepath.Dir(o.tempPatternsFolder), os.ModePerm); err != nil {
return fmt.Errorf("failed to create temp directory: %w", err)
}
// ... retrieves the branch pointed by HEAD
var ref *plumbing.Reference
if ref, err = r.Head(); err != nil {
fmt.Println(err)
return
}
// ... retrieves the commit history for /patterns folder
var cIter object.CommitIter
if cIter, err = r.Log(&git.LogOptions{
From: ref.Hash(),
PathFilter: func(path string) bool {
return path == o.DefaultFolder.Value || strings.HasPrefix(path, o.pathPatternsPrefix)
},
}); err != nil {
fmt.Println(err)
return err
}
var changes []fsdb.DirectoryChange
// ... iterates over the commits
if err = cIter.ForEach(func(c *object.Commit) (err error) {
// GetApplyVariables the files changed in this commit by comparing with its parents
parentIter := c.Parents()
if err = parentIter.ForEach(func(parent *object.Commit) (err error) {
var patch *object.Patch
if patch, err = parent.Patch(c); err != nil {
fmt.Println(err)
return
}
for _, fileStat := range patch.Stats() {
if strings.HasPrefix(fileStat.Name, o.pathPatternsPrefix) {
dir := filepath.Dir(fileStat.Name)
changes = append(changes, fsdb.DirectoryChange{Dir: dir, Timestamp: c.Committer.When})
}
}
return
}); err != nil {
fmt.Println(err)
return
}
return
}); err != nil {
fmt.Println(err)
return
}
// Sort changes by timestamp
sort.Slice(changes, func(i, j int) bool {
return changes[i].Timestamp.Before(changes[j].Timestamp)
// Use the helper to fetch files
err = githelper.FetchFilesFromRepo(githelper.FetchOptions{
RepoURL: o.DefaultGitRepoUrl.Value,
PathPrefix: o.DefaultFolder.Value,
DestDir: o.tempPatternsFolder,
})
if err = o.makeUniqueList(changes); err != nil {
return
if err != nil {
return fmt.Errorf("failed to download patterns: %w", err)
}
var commit *object.Commit
if commit, err = r.CommitObject(ref.Hash()); err != nil {
fmt.Println(err)
return
}
var tree *object.Tree
if tree, err = commit.Tree(); err != nil {
fmt.Println(err)
return
}
if err = tree.Files().ForEach(func(f *object.File) (err error) {
if strings.HasPrefix(f.Name, o.pathPatternsPrefix) {
// Create the local file path
localPath := filepath.Join(os.TempDir(), f.Name)
// Create the directories if they don't exist
if err = os.MkdirAll(filepath.Dir(localPath), os.ModePerm); err != nil {
fmt.Println(err)
return
}
// Write the file to the local filesystem
var blob *object.Blob
if blob, err = r.BlobObject(f.Hash); err != nil {
fmt.Println(err)
return
}
err = o.writeBlobToFile(blob, localPath)
return
}
return
}); err != nil {
fmt.Println(err)
}
return
}
func (o *PatternsLoader) writeBlobToFile(blob *object.Blob, path string) (err error) {
var reader io.ReadCloser
if reader, err = blob.Reader(); err != nil {
return
}
defer reader.Close()
// Create the file
var file *os.File
if file, err = os.Create(path); err != nil {
return
}
defer file.Close()
// Copy the contents of the blob to the file
if _, err = io.Copy(file, reader); err != nil {
return
}
return
}
func (o *PatternsLoader) makeUniqueList(changes []fsdb.DirectoryChange) (err error) {
uniqueItems := make(map[string]bool)
for _, change := range changes {
if strings.TrimSpace(change.Dir) != "" && !strings.Contains(change.Dir, "=>") {
pattern := strings.ReplaceAll(change.Dir, o.pathPatternsPrefix, "")
pattern = strings.TrimSpace(pattern)
uniqueItems[pattern] = true
}
}
finalList := make([]string, 0, len(uniqueItems))
for _, change := range changes {
pattern := strings.ReplaceAll(change.Dir, o.pathPatternsPrefix, "")
pattern = strings.TrimSpace(pattern)
if _, exists := uniqueItems[pattern]; exists {
finalList = append(finalList, pattern)
delete(uniqueItems, pattern) // Remove to avoid duplicates in the final list
}
}
joined := strings.Join(finalList, "\n")
err = os.WriteFile(o.Patterns.UniquePatternsFilePath, []byte(joined), 0o644)
return
return nil
}