// Copyright 2026 Alibaba Group // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cli import ( "fmt" "sort" "strings" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/cmdutil" "github.com/spf13/cobra" "github.com/spf13/pflag" ) // ParamAliasEntry is the reduced parameter-alias table for one runnable Cobra // leaf. It is the typed value the build-time generator serializes into // param_aliases_generated.go and that the runtime normalizer (P2) consumes. // // Aliases maps an already-morphed emitted name to the command's canonical real // flag; the runtime looks up Morph(emitted) here to resolve a synonym. Blocked // lists morphed names that must never be reduced (they route to did-you-mean), // and Ambiguous lists morphed names that a reviewed co-occurrence guard leaves // unresolved on purpose. type ParamAliasEntry struct { CLIPath string `json:"cli_path"` Aliases map[string]string `json:"aliases,omitempty"` Blocked []string `json:"blocked,omitempty"` Ambiguous []string `json:"ambiguous,omitempty"` } // ReduceParamAliases resolves the reviewed concept dictionary against every // runnable leaf's real flags and returns the per-command alias table. It is the // single source of the reduction algorithm, shared by the generator and tests // so the build-time (intersection) and generated views can never disagree. // // The reduction is deliberately mechanical (no NLU): for each concept it morphs // the concept members (plus any command-bound generic flag) and intersects them // with the command's morphed real flags. An intersection of exactly one real // flag yields aliases onto it; two or more real flags is a co-occurrence that // must be an explicitly reviewed `ambiguous` entry or generation fails. Command // scoped aliases override, blocks are removed and recorded, and every override // path and target is validated against the live tree. func ReduceParamAliases(root *cobra.Command) ([]ParamAliasEntry, error) { concepts, err := LoadParamConcepts() if err != nil { return nil, fmt.Errorf("load reviewed parameter concepts: %w", err) } if root == nil { return nil, fmt.Errorf("parameter alias source root is nil") } overrideByPath := make(map[string]CommandOverride, len(concepts.Overrides)) for _, ov := range concepts.Overrides { overrideByPath[ov.CommandPath] = ov } usedOverride := make(map[string]bool, len(overrideByPath)) conceptsByPath := make(map[string][]Concept) usedConceptScope := make(map[string]bool) for _, concept := range concepts.Concepts { for _, path := range concept.Commands { conceptsByPath[path] = append(conceptsByPath[path], concept) } } var problems []string var entries []ParamAliasEntry walkRunnableParamCommands(root, func(leaf *cobra.Command) { path := normalizeSchemaCLIPath(leaf.CommandPath()) realByMorph := realFlagsByMorph(leaf) ov, hasOverride := overrideByPath[path] if hasOverride { usedOverride[path] = true } scopedConcepts := conceptsByPath[path] for _, concept := range scopedConcepts { usedConceptScope[concept.ID+"\x00"+path] = true if !conceptHasRealFlag(concept, ov, realByMorph) { problems = append(problems, fmt.Sprintf("concept %q reviewed command %q has no matching real flag or reviewed bind", concept.ID, path)) } } entry, entryProblems := reduceLeafParamAliases(path, realByMorph, scopedConcepts, ov) problems = append(problems, entryProblems...) if entry != nil { entries = append(entries, *entry) } }) for path := range overrideByPath { if !usedOverride[path] { problems = append(problems, fmt.Sprintf("command_override %q does not match any runnable Cobra leaf", path)) } } for _, concept := range concepts.Concepts { for _, path := range concept.Commands { if !usedConceptScope[concept.ID+"\x00"+path] { problems = append(problems, fmt.Sprintf("concept %q command scope %q does not match any runnable Cobra command", concept.ID, path)) } } } if len(problems) > 0 { sort.Strings(problems) return nil, fmt.Errorf("parameter alias reduction failed:\n - %s", strings.Join(problems, "\n - ")) } sort.Slice(entries, func(i, j int) bool { return entries[i].CLIPath < entries[j].CLIPath }) return entries, nil } // walkRunnableParamCommands invokes fn for every runnable command in the tree, // including runnable parents such as `chat group members` that expose their own // flags while also owning subcommands. Parameter aliasing applies to any command // that accepts flags, which is broader than the schema's leaf-only traversal. func walkRunnableParamCommands(root *cobra.Command, fn func(*cobra.Command)) { if root == nil { return } var walk func(*cobra.Command) walk = func(cmd *cobra.Command) { if cmd.Runnable() { fn(cmd) } for _, sub := range cmd.Commands() { if sub.Name() == "help" { continue } if !sub.IsAvailableCommand() && !hasRuntimeSchemaCommand(sub) { continue } walk(sub) } } walk(root) } // realFlag is one of a leaf's real flags, remembering whether it is hidden so // the reduction can treat a hidden legacy alias flag (for example a hand-written // --base living next to the visible --base-id) as an absorbable synonym rather // than a genuine co-occurrence. type realFlag struct { name string hidden bool } // realFlagsByMorph maps each of a leaf's real flags (local + inherited) by its // morphed name to the real flags that share that morph. func realFlagsByMorph(leaf *cobra.Command) map[string][]realFlag { byMorph := make(map[string][]realFlag) visitAgentExampleCommandFlags(leaf, func(flag *pflag.Flag) { if flag == nil || flag.Name == "help" { return } key := cmdutil.Morph(flag.Name) byMorph[key] = appendRealFlag(byMorph[key], realFlag{name: flag.Name, hidden: flag.Hidden}) }) return byMorph } // reduceLeafParamAliases computes one leaf's alias entry and returns any // contract problems. A nil entry means the leaf produced no aliases, blocks, or // ambiguous guards. func reduceLeafParamAliases(path string, realByMorph map[string][]realFlag, concepts []Concept, ov CommandOverride) (*ParamAliasEntry, []string) { var problems []string aliasMap := make(map[string]string) blockedSet := make(map[string]bool) excludedSet := make(map[string]bool) claimedRealSet := make(map[string]bool) pendingReview := ov.Confirm || ov.Investigate for boundFlag, conceptID := range ov.Bind { if _, ok := realByMorph[cmdutil.Morph(boundFlag)]; !ok { problems = append(problems, fmt.Sprintf("command_override %q binds %q to concept %q but %q is not a real flag", path, boundFlag, conceptID, boundFlag)) } } // (a) Concept auto-reduction. The caller has already admitted only the // concepts whose reviewed command scope contains this exact leaf. for _, concept := range concepts { eff := make(map[string]bool, len(concept.Members)+2) for _, member := range concept.Members { eff[cmdutil.Morph(member)] = true } for boundFlag, conceptID := range ov.Bind { if conceptID == concept.ID { if pendingReview { for _, member := range concept.Members { morphed := cmdutil.Morph(member) if _, isReal := realByMorph[morphed]; !isReal { blockedSet[morphed] = true } } } else { eff[cmdutil.Morph(boundFlag)] = true } } } // Gather the concept's candidate real flags on this command, then // choose a canonical. A single visible real flag wins and absorbs the // rest (including hidden legacy alias flags). Two or more visible real // flags is a genuine co-occurrence that must be reviewed. var candidates []realFlag for key := range eff { candidates = append(candidates, realByMorph[key]...) } if len(candidates) == 0 { continue } for m := range eff { if _, isReal := realByMorph[m]; isReal { claimedRealSet[m] = true } } for _, exclude := range concept.Excludes { excludedSet[cmdutil.Morph(exclude)] = true } visible := distinctRealNames(candidates, true) var canon string switch len(visible) { case 1: canon = visible[0] case 0: names := distinctRealNames(candidates, false) if len(names) != 1 { continue } canon = names[0] default: // Genuine co-occurrence: this concept intersects two or more // visible real flags, so it cannot be auto-reduced. Require that // every emittable synonym of THIS concept (a concept member that is // not itself a real flag on the command) is acknowledged in the // reviewed ambiguous whitelist. Checking only that the command has // some ambiguous entry would let one concept's whitelist silently // vouch for a different concept's unreviewed co-occurrence. ambiguousSet := make(map[string]bool, len(ov.Ambiguous)) for _, a := range ov.Ambiguous { ambiguousSet[cmdutil.Morph(a)] = true } var unreviewed []string for m := range eff { if _, isReal := realByMorph[m]; isReal { continue } if !ambiguousSet[m] { unreviewed = append(unreviewed, m) } } if len(unreviewed) > 0 { sort.Strings(visible) sort.Strings(unreviewed) problems = append(problems, fmt.Sprintf("command %q concept %q intersects visible real flags %s; unreviewed emittable members %s must be listed in the ambiguous whitelist", path, concept.ID, strings.Join(visible, ","), strings.Join(unreviewed, ","))) } continue } for m := range eff { if _, isReal := realByMorph[m]; isReal { continue } if prev, ok := aliasMap[m]; ok && prev != canon { problems = append(problems, fmt.Sprintf("command %q emitted %q reduces to both %q and %q", path, m, prev, canon)) continue } aliasMap[m] = canon } } for excluded := range excludedSet { if _, isAlias := aliasMap[excluded]; isAlias { continue } if claimedRealSet[excluded] { continue } if _, isReal := realByMorph[excluded]; isReal { continue } blockedSet[excluded] = true } // (b) Command scoped aliases override concept reductions. for emitted, target := range ov.ScopedAliases { morphedEmitted := cmdutil.Morph(emitted) reals, ok := realByMorph[cmdutil.Morph(target)] if !ok { problems = append(problems, fmt.Sprintf("command_override %q scoped alias %q->%q targets %q which is not a real flag", path, emitted, target, target)) continue } if _, sourceIsReal := realByMorph[morphedEmitted]; sourceIsReal { problems = append(problems, fmt.Sprintf("command_override %q scoped alias source %q is already a real flag; keep its native compatibility path or remove it before enabling semantic rewrite", path, emitted)) continue } if pendingReview { delete(aliasMap, morphedEmitted) blockedSet[morphedEmitted] = true continue } delete(blockedSet, morphedEmitted) aliasMap[morphedEmitted] = canonicalRealName(reals) } // (c) Blocks are removed from the alias map and recorded for did-you-mean. for _, b := range ov.Block { mb := cmdutil.Morph(b) if _, isReal := realByMorph[mb]; isReal { problems = append(problems, fmt.Sprintf("command_override %q blocks %q but it is already a real flag; blocking must not disable a canonical/native parameter", path, b)) continue } delete(aliasMap, mb) blockedSet[mb] = true } ambiguous := make([]string, 0, len(ov.Ambiguous)) for _, a := range ov.Ambiguous { ma := cmdutil.Morph(a) if _, isReal := realByMorph[ma]; isReal { problems = append(problems, fmt.Sprintf("command_override %q marks %q ambiguous but it is already a real flag", path, a)) continue } if canon, ok := aliasMap[ma]; ok { problems = append(problems, fmt.Sprintf("command %q name %q is both auto-reduced to %q and marked ambiguous; a name cannot be aliased and ambiguous at once", path, a, canon)) } delete(aliasMap, ma) delete(blockedSet, ma) ambiguous = append(ambiguous, ma) } blocked := make([]string, 0, len(blockedSet)) for b := range blockedSet { blocked = append(blocked, b) } if len(aliasMap) == 0 && len(blocked) == 0 && len(ambiguous) == 0 { return nil, problems } return &ParamAliasEntry{ CLIPath: path, Aliases: aliasMap, Blocked: sortedUnique(blocked), Ambiguous: sortedUnique(ambiguous), }, problems } func conceptHasRealFlag(concept Concept, ov CommandOverride, realByMorph map[string][]realFlag) bool { for _, member := range concept.Members { if _, ok := realByMorph[cmdutil.Morph(member)]; ok { return true } } for boundFlag, conceptID := range ov.Bind { if conceptID == concept.ID { if _, ok := realByMorph[cmdutil.Morph(boundFlag)]; ok { return true } } } return false } func appendRealFlag(list []realFlag, value realFlag) []realFlag { for i, existing := range list { if existing.name == value.name { // Prefer the visible record if any registration is visible. if existing.hidden && !value.hidden { list[i] = value } return list } } list = append(list, value) sort.Slice(list, func(i, j int) bool { return list[i].name < list[j].name }) return list } // distinctRealNames returns the sorted unique flag names among candidates, // optionally restricted to visible (non-hidden) flags. func distinctRealNames(candidates []realFlag, visibleOnly bool) []string { seen := make(map[string]bool, len(candidates)) out := make([]string, 0, len(candidates)) for _, c := range candidates { if visibleOnly && c.hidden { continue } if seen[c.name] { continue } seen[c.name] = true out = append(out, c.name) } sort.Strings(out) return out } // canonicalRealName chooses the canonical target among real flags sharing a // morph key, preferring a visible flag over a hidden legacy alias. func canonicalRealName(reals []realFlag) string { for _, r := range reals { if !r.hidden { return r.name } } if len(reals) > 0 { return reals[0].name } return "" } func sortedUnique(values []string) []string { if len(values) == 0 { return nil } seen := make(map[string]bool, len(values)) out := make([]string, 0, len(values)) for _, v := range values { if seen[v] { continue } seen[v] = true out = append(out, v) } sort.Strings(out) return out }