diff --git a/internal/analyze/zip.go b/internal/analyze/zip.go index 6a6cc67..3ab07ad 100644 --- a/internal/analyze/zip.go +++ b/internal/analyze/zip.go @@ -12,20 +12,30 @@ import ( // skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, - "node_modules": true, - "vendor": true, - "__pycache__": true, - ".venv": true, - "venv": true, - "dist": true, - "build": true, - "target": true, - ".next": true, - ".nuxt": true, - "coverage": true, - ".terraform": true, - ".tox": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } // createZip archives the repository at dir into a temporary ZIP file and diff --git a/internal/archdocs/zip.go b/internal/archdocs/zip.go index 07066de..a6f2e18 100644 --- a/internal/archdocs/zip.go +++ b/internal/archdocs/zip.go @@ -12,20 +12,30 @@ import ( // skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, - "node_modules": true, - "vendor": true, - "__pycache__": true, - ".venv": true, - "venv": true, - "dist": true, - "build": true, - "target": true, - ".next": true, - ".nuxt": true, - "coverage": true, - ".terraform": true, - ".tox": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } // createZip archives the repository at dir into a temporary ZIP file and diff --git a/internal/blastradius/zip.go b/internal/blastradius/zip.go index 4769966..5e33519 100644 --- a/internal/blastradius/zip.go +++ b/internal/blastradius/zip.go @@ -12,20 +12,30 @@ import ( // skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, - "node_modules": true, - "vendor": true, - "__pycache__": true, - ".venv": true, - "venv": true, - "dist": true, - "build": true, - "target": true, - ".next": true, - ".nuxt": true, - "coverage": true, - ".terraform": true, - ".tox": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } // createZip archives the repository at dir into a temporary ZIP file and diff --git a/internal/deadcode/zip.go b/internal/deadcode/zip.go index 06c82a7..39ea8c0 100644 --- a/internal/deadcode/zip.go +++ b/internal/deadcode/zip.go @@ -12,20 +12,30 @@ import ( // skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, - "node_modules": true, - "vendor": true, - "__pycache__": true, - ".venv": true, - "venv": true, - "dist": true, - "build": true, - "target": true, - ".next": true, - ".nuxt": true, - "coverage": true, - ".terraform": true, - ".tox": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } // createZip archives the repository at dir into a temporary ZIP file and diff --git a/internal/factory/zip.go b/internal/factory/zip.go index 8e49ef3..32eed79 100644 --- a/internal/factory/zip.go +++ b/internal/factory/zip.go @@ -11,20 +11,30 @@ import ( // skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, - "node_modules": true, - "vendor": true, - "__pycache__": true, - ".venv": true, - "venv": true, - "dist": true, - "build": true, - "target": true, - ".next": true, - ".nuxt": true, - "coverage": true, - ".terraform": true, - ".tox": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } // CreateZip archives the repository at dir into a temporary ZIP file and diff --git a/internal/find/zip.go b/internal/find/zip.go index 0225c57..748fe47 100644 --- a/internal/find/zip.go +++ b/internal/find/zip.go @@ -1,7 +1,5 @@ package find -// zip.go duplicates archive helpers to preserve vertical slice isolation. - import ( "archive/zip" "fmt" @@ -12,25 +10,53 @@ import ( "strings" ) +// skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, "node_modules": true, "vendor": true, - "__pycache__": true, ".venv": true, "venv": true, - "dist": true, "build": true, "target": true, - ".next": true, ".terraform": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } +// createZip archives the repository at dir into a temporary ZIP file and +// returns its path. The caller is responsible for removing the file. +// +// Strategy: use git archive when inside a Git repo (respects .gitignore, +// deterministic output). Falls back to a manual directory walk otherwise. func createZip(dir string) (string, error) { - f, err := os.CreateTemp("", "supermodel-find-*.zip") + f, err := os.CreateTemp("", "supermodel-*.zip") if err != nil { - return "", fmt.Errorf("create temp: %w", err) + return "", fmt.Errorf("create temp file: %w", err) } dest := f.Name() f.Close() + if isGitRepo(dir) { if err := gitArchive(dir, dest); err == nil { return dest, nil } } + if err := walkZip(dir, dest); err != nil { os.Remove(dest) return "", err @@ -51,19 +77,26 @@ func gitArchive(dir, dest string) error { return cmd.Run() } +// walkZip creates a ZIP of dir, excluding skipDirs, hidden files, and +// files larger than 10 MB. func walkZip(dir, dest string) error { out, err := os.Create(dest) if err != nil { return err } defer out.Close() + zw := zip.NewWriter(out) defer zw.Close() + return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } - rel, _ := filepath.Rel(dir, path) + rel, err := filepath.Rel(dir, path) + if err != nil { + return err + } if info.IsDir() { if skipDirs[info.Name()] { return filepath.SkipDir diff --git a/internal/focus/zip.go b/internal/focus/zip.go index 84907d2..83ed7d9 100644 --- a/internal/focus/zip.go +++ b/internal/focus/zip.go @@ -1,8 +1,5 @@ package focus -// zip.go duplicates the archive helpers from analyze/zip.go to preserve -// vertical slice isolation (focus must not import the analyze slice). - import ( "archive/zip" "fmt" @@ -16,25 +13,53 @@ import ( "github.com/supermodeltools/cli/internal/config" ) +// skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, "node_modules": true, "vendor": true, - "__pycache__": true, ".venv": true, "venv": true, - "dist": true, "build": true, "target": true, - ".next": true, ".terraform": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } +// createZip archives the repository at dir into a temporary ZIP file and +// returns its path. The caller is responsible for removing the file. +// +// Strategy: use git archive when inside a Git repo (respects .gitignore, +// deterministic output). Falls back to a manual directory walk otherwise. func createZip(dir string) (string, error) { - f, err := os.CreateTemp("", "supermodel-focus-*.zip") + f, err := os.CreateTemp("", "supermodel-*.zip") if err != nil { - return "", fmt.Errorf("create temp: %w", err) + return "", fmt.Errorf("create temp file: %w", err) } dest := f.Name() f.Close() + if isGitRepo(dir) { if err := gitArchive(dir, dest); err == nil { return dest, nil } } + if err := walkZip(dir, dest); err != nil { os.Remove(dest) return "", err @@ -55,19 +80,26 @@ func gitArchive(dir, dest string) error { return cmd.Run() } +// walkZip creates a ZIP of dir, excluding skipDirs, hidden files, and +// files larger than 10 MB. func walkZip(dir, dest string) error { out, err := os.Create(dest) if err != nil { return err } defer out.Close() + zw := zip.NewWriter(out) defer zw.Close() + return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } - rel, _ := filepath.Rel(dir, path) + rel, err := filepath.Rel(dir, path) + if err != nil { + return err + } if info.IsDir() { if skipDirs[info.Name()] { return filepath.SkipDir @@ -91,6 +123,7 @@ func walkZip(dir, dest string) error { }) } +// newAPIClient wraps api.New for use within the focus slice. func newAPIClient(cfg *config.Config) *api.Client { return api.New(cfg) } diff --git a/internal/mcp/zip.go b/internal/mcp/zip.go index 756cfc3..5b2b08a 100644 --- a/internal/mcp/zip.go +++ b/internal/mcp/zip.go @@ -1,10 +1,5 @@ package mcp -// createZip is a thin shim so the mcp package can archive the repo -// without importing the analyze slice. It delegates to git archive -// and falls back to a directory walk — identical logic to analyze/zip.go -// but duplicated to preserve vertical slice isolation. - import ( "archive/zip" "fmt" @@ -15,22 +10,41 @@ import ( "strings" ) +// skipDirs are directory names that should never be included in the archive. var skipDirs = map[string]bool{ - ".git": true, - "node_modules": true, - "vendor": true, - "__pycache__": true, - ".venv": true, - "venv": true, - "dist": true, - "build": true, - "target": true, - ".next": true, - ".terraform": true, + ".git": true, + ".claude": true, + ".idea": true, + ".vscode": true, + ".cache": true, + ".turbo": true, + ".nx": true, + ".next": true, + ".nuxt": true, + ".terraform": true, + ".tox": true, + ".venv": true, + ".pnpm-store": true, + "__pycache__": true, + "__snapshots__": true, + "bower_components": true, + "build": true, + "coverage": true, + "dist": true, + "node_modules": true, + "out": true, + "target": true, + "vendor": true, + "venv": true, } +// createZip archives the repository at dir into a temporary ZIP file and +// returns its path. The caller is responsible for removing the file. +// +// Strategy: use git archive when inside a Git repo (respects .gitignore, +// deterministic output). Falls back to a manual directory walk otherwise. func createZip(dir string) (string, error) { - f, err := os.CreateTemp("", "supermodel-mcp-*.zip") + f, err := os.CreateTemp("", "supermodel-*.zip") if err != nil { return "", fmt.Errorf("create temp file: %w", err) } @@ -42,6 +56,7 @@ func createZip(dir string) (string, error) { return dest, nil } } + if err := walkZip(dir, dest); err != nil { os.Remove(dest) return "", err @@ -62,14 +77,18 @@ func gitArchive(dir, dest string) error { return cmd.Run() } +// walkZip creates a ZIP of dir, excluding skipDirs, hidden files, and +// files larger than 10 MB. func walkZip(dir, dest string) error { out, err := os.Create(dest) if err != nil { return err } defer out.Close() + zw := zip.NewWriter(out) defer zw.Close() + return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err