package tools import ( "context" "os" "path/filepath" "strings" "testing" ) func writeTree(t *testing.T, root string, files map[string]string) { t.Helper() for name, content := range files { p := filepath.Join(root, name) if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(p, []byte(content), 0o644); err != nil { t.Fatal(err) } } } // TestDiscoverNestedInstructions:只发现目标子树到工作区根之间的 AGENTS.md, // 根自己的那份跳过(它已在基础 system 提示里)。 func TestDiscoverNestedInstructions(t *testing.T) { root := t.TempDir() writeTree(t, root, map[string]string{ "AGENTS.md": "ROOT-RULES", "sub/AGENTS.md": "SUB-RULES", "sub/deep/AGENTS.md": "DEEP-RULES", "sub/deep/x.go": "package deep", }) var got []string discoverInstructions(filepath.Join(root, "sub/deep/x.go"), false, root, func(p, text string) { got = append(got, text) }) joined := strings.Join(got, ",") if !strings.Contains(joined, "DEEP-RULES") || !strings.Contains(joined, "SUB-RULES") { t.Fatalf("nested instructions not discovered: %v", got) } if strings.Contains(joined, "ROOT-RULES") { t.Fatalf("root AGENTS.md must be skipped: %v", got) } if len(got) != 2 || got[0] != "DEEP-RULES" || got[1] != "SUB-RULES" { t.Fatalf("order = %v, want nearest first", got) } // 目录目标:目录自己的 AGENTS.md 也要发现。 got = nil discoverInstructions(filepath.Join(root, "sub"), true, root, func(_ string, text string) { got = append(got, text) }) if len(got) != 1 || got[0] != "SUB-RULES" { t.Fatalf("directory discovery = %v", got) } // 工作区外:不发现。 outside := t.TempDir() writeTree(t, outside, map[string]string{"AGENTS.md": "OUTSIDE"}) got = nil discoverInstructions(filepath.Join(outside, "x.go"), false, root, func(_ string, text string) { got = append(got, text) }) if len(got) != 0 { t.Fatalf("outside instructions leaked: %v", got) } } // TestReadFeedsNestedInstructions:读取子目录文件时,read 把发现的约定交给宿主钩子。 func TestReadFeedsNestedInstructions(t *testing.T) { root := t.TempDir() writeTree(t, root, map[string]string{ "AGENTS.md": "ROOT-RULES", "sub/AGENTS.md": "SUB-RULES", "sub/x.txt": "hello", }) var got []string reg := NewRegistry() for _, tl := range BuiltinWithHooks(root, NewFence(), nil, BuiltinHooks{ OnInstructions: func(path, text string) { got = append(got, text) }, }) { if err := reg.Register(tl); err != nil { t.Fatal(err) } } r, _ := reg.Get("read") if _, err := r.Run(context.Background(), `{"path":"sub/x.txt"}`); err != nil { t.Fatal(err) } if len(got) != 1 || got[0] != "SUB-RULES" { t.Fatalf("read hooks = %v", got) } // 根目录的文件不重复注入根约定。 got = nil if _, err := r.Run(context.Background(), `{"path":"AGENTS.md"}`); err != nil { t.Fatal(err) } if len(got) != 0 { t.Fatalf("root instructions should not be discovered again: %v", got) } }