From cec156c9f1170f870ba115ff9162d722d41a40bf Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 17 Aug 2026 12:42:01 -0700 Subject: [PATCH] =?UTF-8?q?feat(gstack-config):=20`has=20`=20?= =?UTF-8?q?=E2=80=94=20key-presence=20provenance=20through=20STATE=5FDIR?= =?UTF-8?q?=20resolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `get` returns the DEFAULTS value for absent keys, so callers that need to know whether the USER decided something (vs inherited a default) had no correct primitive — setup's consent logic was about to grep a hardcoded ~/.gstack/config.yaml, which misclassifies under GSTACK_STATE_ROOT / GSTACK_HOME / GSTACK_STATE_DIR overrides. `has` exits 0 iff the key is literally present in the resolved config file, with the same C-locale key validation as get/set. Co-Authored-By: Claude Fable 5 --- bin/gstack-config | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/gstack-config b/bin/gstack-config index b8adf9c25..0df6505b1 100755 --- a/bin/gstack-config +++ b/bin/gstack-config @@ -3,6 +3,9 @@ # # Usage: # gstack-config get — read a config value (falls back to DEFAULTS) +# gstack-config has — exit 0 iff the key is literally present in the +# config file (get returns DEFAULTS for absent keys, +# so callers that need provenance use this instead) # gstack-config set — write a config value # gstack-config list — show all config (values + defaults) # gstack-config defaults — show just the defaults table @@ -301,6 +304,14 @@ case "${1:-}" in fi printf '%s' "$VALUE" ;; + has) + KEY="${2:?Usage: gstack-config has }" + if ! printf '%s' "$KEY" | LC_ALL=C grep -qE '^[a-zA-Z0-9_]+(@[a-zA-Z0-9]+)?$'; then + echo "Error: key must contain only alphanumeric characters, underscores, and an optional @ suffix" >&2 + exit 1 + fi + grep -qE "^${KEY}:" "$CONFIG_FILE" 2>/dev/null + ;; set) KEY="${2:?Usage: gstack-config set }" VALUE="${3:?Usage: gstack-config set }"