study & make comments

This commit is contained in:
2026-05-15 23:54:10 +08:00
parent 9079152812
commit 9587ac85b7
5 changed files with 90 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+40
View File
@@ -11,12 +11,35 @@ import (
const maxFileSize = 1 * 1024 * 1024 // 1MB const maxFileSize = 1 * 1024 * 1024 // 1MB
// Build 根据 config 中定义的 bundle key,将多个文件内容聚合成一个字符串
//
// 设计用途:
// 用于 CLI 工具中快速“打包”一组文件内容,例如:
// cliprepo bundle api
// => 输出 api 相关的一组代码文件内容,方便复制/分享/提交 prompt
//
// 行为特点:
// 1. 从 cliprepo.yaml 中读取 bundles 配置
// 2. 根据 key 找到对应文件列表
// 3. 逐个读取文件内容并拼接
// 4. 自动跳过异常文件(不存在 / 读取失败)
// 5. 自动过滤过大的文件(> 1MB)
//
// 输出格式:
//
// ===== path/to/file =====
// <file content>
//
// ===== path/to/another =====
// <file content>
func Build(key string) (string, error) { func Build(key string) (string, error) {
// 读取配置文件(cliprepo.yaml
cfg, err := config.Load() cfg, err := config.Load()
if err != nil { if err != nil {
return "", err return "", err
} }
// 获取对应 bundle 的文件列表
files, ok := cfg.Bundles[key] files, ok := cfg.Bundles[key]
if !ok { if !ok {
return "", fmt.Errorf("bundle not found: %s", key) return "", fmt.Errorf("bundle not found: %s", key)
@@ -24,22 +47,31 @@ func Build(key string) (string, error) {
var builder strings.Builder var builder strings.Builder
// 遍历 bundle 中的所有文件路径
for _, file := range files { for _, file := range files {
// 获取文件信息(用于判断大小 / 是否存在)
info, err := os.Stat(file) info, err := os.Stat(file)
if err != nil { if err != nil {
// 文件不存在或无法访问时跳过
continue continue
} }
// 跳过过大的文件,避免 CLI 输出过载或内存占用过高
if info.Size() > maxFileSize { if info.Size() > maxFileSize {
continue continue
} }
// 读取文件内容
data, err := os.ReadFile(file) data, err := os.ReadFile(file)
if err != nil { if err != nil {
// 读取失败直接跳过,不中断整个 bundle 构建
continue continue
} }
// 写入文件分隔标记,便于阅读结构
builder.WriteString(fmt.Sprintf("===== %s =====\n", file)) builder.WriteString(fmt.Sprintf("===== %s =====\n", file))
// 写入文件内容
builder.Write(data) builder.Write(data)
builder.WriteString("\n\n") builder.WriteString("\n\n")
} }
@@ -47,6 +79,14 @@ func Build(key string) (string, error) {
return builder.String(), nil return builder.String(), nil
} }
// Copy 将 bundle 内容写入系统剪贴板
//
// 常见使用场景:
// CLI 一键复制 bundle 输出,用于:
// - prompt 输入
// - 代码审查
// - 远程分享
// - AI 上下文输入
func Copy(content string) error { func Copy(content string) error {
return clipboard.WriteAll(content) return clipboard.WriteAll(content)
} }
+16
View File
@@ -6,17 +6,33 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Config 定义 cliprepo 的配置结构
//
// 当前支持的配置:
// Bundles:用于定义一组文件路径集合
// key -> bundle 名称
// value -> 对应的文件路径列表
type Config struct { type Config struct {
Bundles map[string][]string `yaml:"bundles"` Bundles map[string][]string `yaml:"bundles"`
} }
// Load 从项目根目录读取 cliprepo.yaml 配置文件,并解析为 Config 结构
//
// 行为说明:
// 1. 默认读取当前工作目录下的 cliprepo.yaml
// 2. 如果文件不存在或读取失败,直接返回 error
// 3. 使用 YAML 解析配置内容到 Config 结构体
func Load() (*Config, error) { func Load() (*Config, error) {
// 读取配置文件内容
data, err := os.ReadFile("cliprepo.yaml") data, err := os.ReadFile("cliprepo.yaml")
if err != nil { if err != nil {
return nil, err return nil, err
} }
var cfg Config var cfg Config
// 解析 YAML 到结构体
err = yaml.Unmarshal(data, &cfg) err = yaml.Unmarshal(data, &cfg)
return &cfg, err return &cfg, err
} }
+10
View File
@@ -7,12 +7,22 @@ import (
ignore "github.com/sabhiram/go-gitignore" ignore "github.com/sabhiram/go-gitignore"
) )
// Load 加载指定 root 目录下的 .gitignore 规则
//
// 行为说明:
// 1. 如果 root/.gitignore 存在,则解析该文件
// 2. 如果不存在,则返回一个“空规则集”(不会忽略任何文件)
// 3. 返回 go-gitignore 的 GitIgnore 对象,用于路径匹配
func Load(root string) (*ignore.GitIgnore, error) { func Load(root string) (*ignore.GitIgnore, error) {
// 拼接 .gitignore 文件路径
path := filepath.Join(root, ".gitignore") path := filepath.Join(root, ".gitignore")
// 如果 .gitignore 不存在,则返回一个空的 ignore 规则
// 这样后续 MatchesPath 不会 panic,也不会过滤任何文件
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
return ignore.CompileIgnoreLines(), nil return ignore.CompileIgnoreLines(), nil
} }
// 如果存在 .gitignore,则解析文件内容生成规则
return ignore.CompileIgnoreFile(path) return ignore.CompileIgnoreFile(path)
} }
+24
View File
@@ -10,23 +10,35 @@ import (
"github.com/atotto/clipboard" "github.com/atotto/clipboard"
) )
// Generate 从 root 目录开始遍历文件树,并生成类似 `tree` 命令的目录结构字符串
//
// 功能特点:
// 1. 递归遍历目录
// 2. 支持 .gitignore 规则过滤
// 3. 输出树形结构字符串
func Generate(root string) (string, error) { func Generate(root string) (string, error) {
// 加载 gitignore 规则(如果失败则忽略错误,默认不过滤)
ig, _ := gitignore.Load(root) ig, _ := gitignore.Load(root)
var builder strings.Builder var builder strings.Builder
// 递归遍历目录结构
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
// 根目录特殊处理,统一显示为 "."
if path == root { if path == root {
builder.WriteString(".\n") builder.WriteString(".\n")
return nil return nil
} }
// 转换为相对路径,便于匹配 gitignore 和计算层级
rel, _ := filepath.Rel(root, path) rel, _ := filepath.Rel(root, path)
// 如果路径匹配 gitignore 规则,则跳过
// 如果是目录,则直接跳过整个子树
if ig.MatchesPath(rel) { if ig.MatchesPath(rel) {
if info.IsDir() { if info.IsDir() {
return filepath.SkipDir return filepath.SkipDir
@@ -34,9 +46,14 @@ func Generate(root string) (string, error) {
return nil return nil
} }
// 计算当前节点在目录树中的层级
// 通过路径分隔符数量判断深度
level := strings.Count(rel, string(os.PathSeparator)) level := strings.Count(rel, string(os.PathSeparator))
// 根据层级生成缩进
prefix := strings.Repeat("│ ", level) prefix := strings.Repeat("│ ", level)
// 写入当前节点(文件或目录)
builder.WriteString(fmt.Sprintf("%s├── %s\n", prefix, info.Name())) builder.WriteString(fmt.Sprintf("%s├── %s\n", prefix, info.Name()))
return nil return nil
}) })
@@ -44,10 +61,17 @@ func Generate(root string) (string, error) {
return builder.String(), err return builder.String(), err
} }
// Copy 将生成的字符串写入系统剪贴板
//
// 常用于 CLI 工具:直接复制 tree 输出结果
func Copy(content string) error { func Copy(content string) error {
return clipboard.WriteAll(content) return clipboard.WriteAll(content)
} }
// Write 将内容写入指定文件路径
//
// 如果文件存在则覆盖
// 权限默认设置为 0644rw-r--r--
func Write(path, content string) error { func Write(path, content string) error {
return os.WriteFile(path, []byte(content), 0644) return os.WriteFile(path, []byte(content), 0644)
} }