// Copyright (c) 2026 Lark Technologies Pte. Ltd. // SPDX-License-Identifier: MIT package sheets import ( _ "embed" "encoding/json" "fmt" "sort" "sync" "github.com/larksuite/cli/errs" ) // ─── --print-schema runtime introspection ───────────────────────────── // // Composite JSON flags (--cells, --properties, --operations, --border-styles, // --sort-keys) carry non-trivial structured payloads. Reference docs cover // the top-level fields but agents often need the full JSON Schema to // generate valid input. // // To serve that need without forcing every caller to fetch external docs, // the spec repo ships a compact `flag-schemas.json` that extracts just the // schema subtree corresponding to each (shortcut, flag) pair. We embed // that artifact at compile time so `lark-cli sheets // --print-schema --flag-name ` runs entirely locally. // // The artifact is generated by sheet-skill-spec's // scripts/sync_to_consumers.mjs from canonical-spec/cli-flag-schema-map.json // + tool-schemas/mcp-tools.json. Do not hand-edit data/flag-schemas.json; // regenerate via the sync script. //go:embed data/flag-schemas.json var flagSchemasJSON []byte // flagSchemaIndex parses lazily on first access; failures are surfaced // as errors from the lookup helper rather than panicking at init time. type flagSchemaIndex struct { SchemaVersion string `json:"schema_version"` Flags map[string]map[string]json.RawMessage `json:"flags"` } // loadFlagSchemas is sync.Once-guarded so concurrent first access from // parallel goroutines (e.g. parallel unit tests, parallel shortcut // invocations) doesn't race on the lazy parse. var ( flagSchemasOnce sync.Once parsedFlagSchemas *flagSchemaIndex parseFlagErr error ) func loadFlagSchemas() (*flagSchemaIndex, error) { flagSchemasOnce.Do(func() { var idx flagSchemaIndex if err := json.Unmarshal(flagSchemasJSON, &idx); err != nil { parseFlagErr = fmt.Errorf("flag-schemas.json: %w", err) return } if idx.Flags == nil { idx.Flags = map[string]map[string]json.RawMessage{} } parsedFlagSchemas = &idx }) return parsedFlagSchemas, parseFlagErr } // commandsWithFlagSchema returns the set of shortcut commands that have // at least one introspectable flag. Used by Shortcuts() to decide which // shortcuts to wire PrintFlagSchema into. func commandsWithFlagSchema() map[string]struct{} { idx, err := loadFlagSchemas() if err != nil || idx == nil { return nil } out := make(map[string]struct{}, len(idx.Flags)) for cmd := range idx.Flags { out[cmd] = struct{}{} } return out } // printFlagSchemaFor returns a PrintFlagSchema closure bound to the given // shortcut command. When flagName == "" the closure returns a JSON // listing of introspectable flags; otherwise it returns the schema // subtree JSON for the named flag, or an error if the flag is not // registered. func printFlagSchemaFor(command string) func(flagName string) ([]byte, error) { return func(flagName string) ([]byte, error) { idx, err := loadFlagSchemas() if err != nil { return nil, err } entry, ok := idx.Flags[command] if !ok || len(entry) == 0 { return nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "no JSON Schema registered for %s", command) } if flagName == "" { flags := make([]string, 0, len(entry)) for f := range entry { flags = append(flags, f) } sort.Strings(flags) return json.MarshalIndent(map[string]interface{}{ "shortcut": command, "introspectable_flags": flags, "hint": "run again with --flag-name to dump the JSON Schema for that flag", }, "", " ") } schema, ok := entry[flagName] if !ok { flags := make([]string, 0, len(entry)) for f := range entry { flags = append(flags, f) } sort.Strings(flags) return nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "no JSON Schema registered for %s --%s; available: %v", command, flagName, flags). WithParam("--flag-name") } // Reformat for readability — schema files store compact JSON. var pretty interface{} if err := json.Unmarshal(schema, &pretty); err != nil { return nil, err } return json.MarshalIndent(pretty, "", " ") } }