feat(schema): generate catalog from live Cobra tree without fallback

Stop registering runtime catalog fallback commands and make command-surface generation use the real Cobra tree directly. Regenerate schema surface, agent metadata and catalog from executable commands (20 products / 537 tools), add runtime-surface completeness hints, and update catalog gates/tests to use dynamic counts instead of old 504/21/461 constants.

This makes schema describe the actual executable CLI surface; drift/catalog gates and go test ./internal/cli ./internal/app ./internal/generator/... pass.
This commit is contained in:
玉澜 committed 2026-07-11 16:07:23 +08:00
1 parent 538754bbba
commit cb4d1c215c
25 files changed
+10990 -7578

No files matched your search

@@ -14,7 +14,6 @@
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
@@ -26,6 +25,7 @@ import (
"strings"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/app"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/generator/agentmetadata"
)
@@ -294,19 +294,81 @@ func loadCommandSurface() (commandSurface, error) {
func currentCommandSurfaceSnapshot() (commandSurfaceSnapshot, error) {
root := app.NewRootCommand()
var stdout, stderr bytes.Buffer
root.SetOut(&stdout)
root.SetErr(&stderr)
root.SetArgs([]string{"schema", "--all", "--format", "json"})
if err := root.Execute(); err != nil {
return commandSurfaceSnapshot{}, fmt.Errorf("execute dws schema --all: %w: %s", err, strings.TrimSpace(stderr.String()))
snapshot, err := cli.BuildSchemaCatalogSnapshot(root, cli.SchemaCatalogBuildOptions{})
if err != nil {
return commandSurfaceSnapshot{}, fmt.Errorf("build command surface from Cobra tree: %w", err)
}
var snapshot commandSurfaceSnapshot
if err := json.Unmarshal(stdout.Bytes(), &snapshot); err != nil {
return commandSurfaceSnapshot{}, fmt.Errorf("decode schema catalog: %w", err)
products := make([]commandSurfaceProduct, 0)
for _, rawProduct := range schemaMapSlice(snapshot.Catalog["products"]) {
productID := strings.TrimSpace(schemaString(rawProduct["id"]))
if productID == "" {
continue
}
product := commandSurfaceProduct{ID: productID}
for _, rawTool := range schemaMapSlice(rawProduct["tools"]) {
cliPath := strings.TrimSpace(schemaString(rawTool["cli_path"]))
if cliPath == "" {
continue
}
aliases := schemaStringSlice(rawTool["aliases"])
product.Tools = append(product.Tools, commandSurfaceTool{
CanonicalPath: strings.TrimSpace(schemaString(rawTool["canonical_path"])),
SourceProductID: strings.TrimSpace(schemaString(rawTool["source_product_id"])),
CLIPath: cliPath,
Aliases: aliases,
})
}
products = append(products, product)
}
snapshot.Version = commandSurfaceSnapshotVersion
return normalizeCommandSurfaceSnapshot(snapshot), nil
return normalizeCommandSurfaceSnapshot(commandSurfaceSnapshot{Version: commandSurfaceSnapshotVersion, Products: products}), nil
}
func schemaMapSlice(value any) []map[string]any {
items, ok := value.([]map[string]any)
if ok {
return items
}
anyItems, ok := value.([]any)
if !ok {
return nil
}
out := make([]map[string]any, 0, len(anyItems))
for _, item := range anyItems {
if mapped, ok := item.(map[string]any); ok {
out = append(out, mapped)
}
}
return out
}
func schemaString(value any) string {
if value == nil {
return ""
}
if s, ok := value.(string); ok {
return s
}
return fmt.Sprint(value)
}
func schemaStringSlice(value any) []string {
if value == nil {
return nil
}
if items, ok := value.([]string); ok {
return append([]string(nil), items...)
}
anyItems, ok := value.([]any)
if !ok {
return nil
}
out := make([]string, 0, len(anyItems))
for _, item := range anyItems {
if s := strings.TrimSpace(schemaString(item)); s != "" {
out = append(out, s)
}
}
return out
}
func loadCommandSurfaceSnapshot(path string) (commandSurface, error) {