OpenCode 实践课:3 分钟用 Go 写出一个命令行目录树可视化工具

你有没有遇到过这种场景:打开一个不熟悉的项目,想快速了解目录结构,却要在 IDE 里一层层展开文件夹?tree 命令可以解决问题,但 Windows 默认不带,Linux 也要单独安装。

今天我们用 OpenCode,用三句自然语言描述,3 分钟写出一个跨平台目录树可视化工具。

项目介绍

用 Go 语言实现一个命令行目录树工具,支持三个核心功能:

  • 树形展示:用 ├──└── 绘制目录层级
  • 深度控制-L 2 限制最大递归深度
  • 文件过滤-d 只显示目录,-a 显示隐藏文件

最终代码约 100 行,无任何第三方依赖。

准备工作

  • 安装 Go 1.18+:go.dev/dl
  • 安装 OpenCode:npm install -g @anthropic/opencode(或用 pip install opencode
  • 在终端执行 opencode 启动交互会话

实践过程

第一步:初始化项目骨架

我直接对 OpenCode 描述需求,让它生成代码骨架:

> 我对 OpenCode 说:
> 用 Go 写一个命令行工具,读取指定目录(默认当前目录),递归遍历所有子目录和文件,用树形结构打印出来。只打印文件名,不打印完整路径。

OpenCode 几秒钟就生成了核心逻辑——用 os.ReadDir 读取目录,递归调用自身,用 ├──└── 做连接符,并根据是否是最后一个条目决定缩进前缀( 还是空格)。

func printTree(path string, prefix string, maxDepth int, depth int, dirsOnly bool, showHidden bool) {
    if maxDepth >= 0 && depth > maxDepth {
        return
    }
    entries, _ := os.ReadDir(path)
    // 排序、过滤、打印...
}

第二步:添加命令行参数

骨架跑通后,我对 OpenCode 说:

> 我对 OpenCode 说:
> 加上命令行参数:-L 控制最大深度(默认 -1 表示不限制),-d 只显示目录不显示文件,-a 显示隐藏文件(以 . 开头的文件和目录)。

OpenCode 用 Go 标准库的 flag 包解析参数,在遍历时增加对应的过滤逻辑——dirsOnly 跳过非目录项,showHidden 控制 . 开头文件的可见性。

func main() {
    maxDepth := flag.Int("L", -1, "最大深度,-1 表示不限制")
    dirsOnly := flag.Bool("d", false, "只显示目录")
    showHidden := flag.Bool("a", false, "显示隐藏文件")
    flag.Parse()
    // ...
}

第三步:美化输出和统计

基础功能完成后,我发现缺少统计信息,于是对 OpenCode 说:

> 我对 OpenCode 说:
> 遍历结束后在最后一行打印统计信息:显示了多少个目录、多少个文件,格式为 N directories, M files

OpenCode 在主函数里增加了两个计数器,递归遍历时分别统计目录和文件数量,最后用 fmt.Printf 输出汇总行。

整个过程我没有手写一行代码,全靠自然语言描述。从说出需求到可运行的工具,前后不到 3 分钟。

完整代码

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"sort"
)

var (
	dirCount  int
	fileCount int
)

func main() {
	maxDepth := flag.Int("L", -1, "最大深度,-1 表示不限制")
	dirsOnly := flag.Bool("d", false, "只显示目录")
	showHidden := flag.Bool("a", false, "显示隐藏文件")
	flag.Parse()

	root := "."
	if flag.NArg() > 0 {
		root = flag.Arg(0)
	}

	fmt.Println(root)
	printTree(root, "", *maxDepth, 0, *dirsOnly, *showHidden)
	fmt.Printf("\n%d directories, %d files\n", dirCount, fileCount)
}

func printTree(path string, prefix string, maxDepth int, depth int, dirsOnly bool, showHidden bool) {
	if maxDepth >= 0 && depth > maxDepth {
		return
	}

	entries, err := os.ReadDir(path)
	if err != nil {
		return
	}

	sort.Slice(entries, func(i, j int) bool {
		return entries[i].Name() < entries[j].Name()
	})

	var filtered []os.DirEntry
	for _, e := range entries {
		if !showHidden && e.Name()[0] == '.' {
			continue
		}
		if dirsOnly && !e.IsDir() {
			continue
		}
		filtered = append(filtered, e)
	}

	for i, entry := range filtered {
		isLast := i == len(filtered)-1
		isDir := entry.IsDir()

		if isDir {
			dirCount++
		} else {
			fileCount++
		}

		connector := "├── "
		if isLast {
			connector = "└── "
		}
		fmt.Printf("%s%s%s\n", prefix, connector, entry.Name())

		if isDir {
			newPrefix := prefix
			if isLast {
				newPrefix += "    "
			} else {
				newPrefix += "│   "
			}
			printTree(filepath.Join(path, entry.Name()), newPrefix, maxDepth, depth+1, dirsOnly, showHidden)
		}
	}
}

运行效果:

$ go run main.go -L 2 -d myproject/
myproject/
├── cmd
│   └── server
├── internal
│   ├── config
│   └── handler
└── pkg
    └── utils

5 directories, 0 files

小结

这个小工具的开发过程展示了 OpenCode 的核心价值:用自然语言驱动开发,让 AI 处理代码细节,人的精力聚焦在需求描述和结果验证上

传统方式写这个工具,你需要回忆 os.ReadDir 的返回值类型、查 sort.Slice 的用法、调试缩进前缀的拼接逻辑——这些细节至少花 15-20 分钟。用 OpenCode,你只需清晰描述想要什么,3 分钟内就能拿到可运行的代码。

更有意思的是迭代过程:你觉得缺点什么,说一句话,OpenCode 就帮你改好。这种"对话式编程"的体验,比传统"打字—编译—报错—修改"的循环高效太多了。

如果你还没试过 OpenCode,不妨从这个小工具开始——相信我,一旦习惯了用嘴编程,你就回不去了。