2740f4a41a
- tool_create_function.go - naming.go - naming_test.go Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
121 lines
4.5 KiB
Go
121 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// canonicalDomains lists registry domains accepted by fn_create_function.
|
|
// Keep in sync with `mcp__registry__fn_list_domains` and `.claude/rules/ids_naming.md`.
|
|
var canonicalDomains = map[string]bool{
|
|
"browser": true,
|
|
"core": true,
|
|
"cybersecurity": true,
|
|
"datascience": true,
|
|
"docker": true,
|
|
"finance": true,
|
|
"gamedev": true,
|
|
"geo": true,
|
|
"gfx": true,
|
|
"infra": true,
|
|
"metabase": true,
|
|
"ml": true,
|
|
"notebook": true,
|
|
"pipelines": true,
|
|
"shell": true,
|
|
"tui": true,
|
|
"ui": true,
|
|
"viz": true,
|
|
}
|
|
|
|
// actionVerbs is the allowlist of verbs that make a function name predictable.
|
|
// See .claude/rules/ids_naming.md. Add new recurring verbs here when introduced.
|
|
var actionVerbs = map[string]bool{}
|
|
|
|
// acronymExceptions are nouns accepted as standalone names because the registry
|
|
// has historical precedent (math/stats indicators).
|
|
var acronymExceptions = map[string]bool{
|
|
"sma": true, "ema": true, "rsi": true, "vwap": true, "adx": true,
|
|
"macd": true, "atr": true, "obv": true, "bollinger": true,
|
|
}
|
|
|
|
func init() {
|
|
for _, v := range []string{
|
|
"get", "set", "list", "find", "search", "show", "read", "load", "fetch", "scan", "query", "lookup",
|
|
"parse", "format", "encode", "decode", "marshal", "unmarshal", "serialize", "deserialize",
|
|
"validate", "check", "ensure", "verify", "audit", "diagnose", "test", "match",
|
|
"filter", "map", "reduce", "sort", "group", "count", "sum", "aggregate", "compute", "calculate",
|
|
"score", "rank", "cluster", "classify", "detect",
|
|
"init", "create", "make", "build", "generate", "scaffold", "install", "setup", "configure", "register",
|
|
"add", "insert", "append", "prepend", "update", "upsert", "modify", "edit", "patch", "replace",
|
|
"delete", "remove", "clear", "drop", "prune", "clean",
|
|
"copy", "move", "rename", "sync", "clone", "extract", "inject", "import", "export",
|
|
"send", "post", "put", "call", "dispatch", "exec", "run", "launch", "start", "stop", "kill",
|
|
"restart", "redeploy", "deploy",
|
|
"open", "close", "connect", "disconnect", "login", "logout", "authenticate",
|
|
"enable", "disable", "toggle", "lock", "unlock",
|
|
"propose", "promote", "deprecate", "approve", "reject", "apply",
|
|
"emit", "render", "draw", "paint", "serve", "host",
|
|
"pull", "push", "checkout", "commit", "tag", "merge", "rebase",
|
|
"watch", "monitor", "observe", "log", "trace", "profile", "benchmark",
|
|
"snapshot", "backup", "restore", "archive", "compress", "decompress",
|
|
"hash", "encrypt", "decrypt", "sign",
|
|
"taskkill", "recopile", "recopilate", "vault",
|
|
"gather", "collect", "fold", "head", "tail", "take", "chunk", "batch",
|
|
"debounce", "throttle", "retry", "await", "sleep", "ping",
|
|
"prime", "warm", "refresh", "invalidate", "reload", "reset", "rollback",
|
|
"fork", "spawn", "daemon",
|
|
"plot", "capture", "replay",
|
|
"slice", "window", "split", "join", "concat", "flatten", "unflatten", "transpose", "reverse",
|
|
"shuffle", "sample", "dedup", "distinct", "unique",
|
|
"audit", "verify", "lint", "format",
|
|
"tally", "bucket", "histogram",
|
|
} {
|
|
actionVerbs[v] = true
|
|
}
|
|
}
|
|
|
|
// validateName checks that name is predictable per ids_naming.md:
|
|
// - snake_case
|
|
// - contains an action verb token, or is a recognized acronym
|
|
// Components (kind=component) skip the verb check.
|
|
func validateName(name, kind string) error {
|
|
if name == "" {
|
|
return fmt.Errorf("name is empty")
|
|
}
|
|
if !isSnakeCase(name) {
|
|
return fmt.Errorf("name must be snake_case (lowercase + digits + underscores), got %q", name)
|
|
}
|
|
if kind == "component" || kind == "type" {
|
|
return nil
|
|
}
|
|
tokens := strings.Split(name, "_")
|
|
for _, t := range tokens {
|
|
if actionVerbs[t] {
|
|
return nil
|
|
}
|
|
}
|
|
// single-token acronym exception
|
|
if len(tokens) == 1 && acronymExceptions[tokens[0]] {
|
|
return nil
|
|
}
|
|
return fmt.Errorf(
|
|
"naming: name %q lacks action verb. Add a verb token (get_X, X_lookup, parse_X, ...). See .claude/rules/ids_naming.md",
|
|
name,
|
|
)
|
|
}
|
|
|
|
// validateDomain checks that domain is in the canonical list.
|
|
func validateDomain(domain string) error {
|
|
if domain == "" {
|
|
return fmt.Errorf("domain is empty")
|
|
}
|
|
if !canonicalDomains[domain] {
|
|
return fmt.Errorf(
|
|
"naming: domain %q not in canonical list. Allowed: browser, core, cybersecurity, datascience, docker, finance, gamedev, geo, gfx, infra, metabase, ml, notebook, pipelines, shell, tui, ui, viz. Add new domain to CLAUDE.md + apps/registry_mcp/naming.go first.",
|
|
domain,
|
|
)
|
|
}
|
|
return nil
|
|
}
|