Move AnnotateRuntime* into cli/runtimeannotate and the Cobra-keyed ContractFinal store into cli/contractfinal so corecmd depends on thin subpackages instead of the cli delivery root. Keep contract as DTO-only, document Description declare-vs-delivery, and house homology gates under cli/homology. Co-authored-by: Cursor <cursoragent@cursor.com>
80 lines
3.2 KiB
Go
80 lines
3.2 KiB
Go
// 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"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// CommandSafety holds the safety metadata for a CLI command, resolved at
|
|
// runtime via ResolveMeta (embedded schema_meta_index.json). This is a
|
|
// read-only view — NOT a second safety source. The Catalog remains the
|
|
// authoritative reviewed ToolSpec wire; the meta index is its Identity /
|
|
// Safety / Selection projection for help and selection consumers.
|
|
type CommandSafety struct {
|
|
Effect string // read / write / destructive
|
|
Risk string // low / medium / high
|
|
Confirmation string // not_required / user_required
|
|
Idempotency string // idempotent / non_idempotent
|
|
}
|
|
|
|
// ShouldRender returns true when the safety metadata warrants a visible
|
|
// annotation in --help. Only commands that need confirmation or carry
|
|
// above-low risk are annotated; read-only low-risk commands stay clean.
|
|
func (s CommandSafety) ShouldRender() bool {
|
|
return s.Confirmation == "user_required" ||
|
|
(s.Risk != "" && s.Risk != "low")
|
|
}
|
|
|
|
// SafetyForCLIPath returns the safety metadata for a command identified by its
|
|
// CLI path (e.g. "dev app delete"). Returns ok=false when the path is absent
|
|
// from the embedded schema_meta_index.json (utility commands, hidden commands,
|
|
// shortcuts). Delegates to ResolveMeta; it does not decode schema_catalog/.
|
|
// A corrupt meta index panics via ResolveMeta (fail-closed).
|
|
//
|
|
// Deprecated: use ResolveMeta(cliPath).Safety for the complete metadata view.
|
|
// Kept for backward compatibility with existing callers.
|
|
func SafetyForCLIPath(cliPath string) (CommandSafety, bool) {
|
|
meta, ok := ResolveMeta(cliPath)
|
|
if !ok {
|
|
return CommandSafety{}, false
|
|
}
|
|
return meta.Safety, true
|
|
}
|
|
|
|
// RenderSafetyAnnotation writes a "Safety:" line to the command's stdout when
|
|
// the command carries above-low risk or requires user confirmation. This is
|
|
// the shared entry point for ALL help rendering paths (root HelpFunc, product
|
|
// group custom HelpFuncs like calendar's). It avoids the timing issue where a
|
|
// group captures origHelp before configureRootHelp sets the root's custom func.
|
|
func RenderSafetyAnnotation(cmd *cobra.Command) {
|
|
cliPath := strings.TrimSpace(strings.TrimPrefix(cmd.CommandPath(), cmd.Root().Name()+" "))
|
|
safety, ok := SafetyForCLIPath(cliPath)
|
|
if !ok || !safety.ShouldRender() {
|
|
return
|
|
}
|
|
w := cmd.OutOrStdout()
|
|
fmt.Fprintf(w, "\nSafety: effect=%s risk=%s confirmation=%s", safety.Effect, safety.Risk, safety.Confirmation)
|
|
if safety.Idempotency != "" {
|
|
fmt.Fprintf(w, " idempotency=%s", safety.Idempotency)
|
|
}
|
|
if safety.Confirmation == "user_required" {
|
|
fmt.Fprint(w, " (requires --yes)")
|
|
}
|
|
fmt.Fprintln(w)
|
|
}
|