Skip to content

Commit 12fe594

Browse files
fix: address remaining PR review findings (bash 3.2 compat, has_jq regression, JSON escaping)
- Fix has_jq regression in create-new-feature.sh: the script does not source common.sh, so replace has_jq with inline command -v jq check - Replace declare -A with linear array in update-agent-context.sh for bash 3.2 compatibility (macOS ships bash 3.2 by default) - Add json_escape() helper and apply to all printf JSON fallback paths in check-prerequisites.sh, setup-plan.sh, and create-new-feature.sh - Use printf '%q' for branch name in stdout text-mode output for consistency with the stderr persistence hint
1 parent abf57c2 commit 12fe594

File tree

5 files changed

+36
-9
lines changed

5 files changed

+36
-9
lines changed

scripts/bash/check-prerequisites.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ if $PATHS_ONLY; then
9999
'{REPO_ROOT:$repo_root,BRANCH:$branch,FEATURE_DIR:$feature_dir,FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,TASKS:$tasks}'
100100
else
101101
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s"}\n' \
102-
"$REPO_ROOT" "$CURRENT_BRANCH" "$FEATURE_DIR" "$FEATURE_SPEC" "$IMPL_PLAN" "$TASKS"
102+
"$(json_escape "$REPO_ROOT")" "$(json_escape "$CURRENT_BRANCH")" "$(json_escape "$FEATURE_DIR")" "$(json_escape "$FEATURE_SPEC")" "$(json_escape "$IMPL_PLAN")" "$(json_escape "$TASKS")"
103103
fi
104104
else
105105
echo "REPO_ROOT: $REPO_ROOT"
@@ -171,7 +171,7 @@ if $JSON_MODE; then
171171
json_docs=$(printf '"%s",' "${docs[@]}")
172172
json_docs="[${json_docs%,}]"
173173
fi
174-
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$FEATURE_DIR" "$json_docs"
174+
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"
175175
fi
176176
else
177177
# Text output

scripts/bash/common.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,18 @@ has_jq() {
160160
command -v jq >/dev/null 2>&1
161161
}
162162

163+
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
164+
# Handles backslash, double-quote, and control characters (newline, tab, carriage return).
165+
json_escape() {
166+
local s="$1"
167+
s="${s//\\/\\\\}"
168+
s="${s//\"/\\\"}"
169+
s="${s//$'\n'/\\n}"
170+
s="${s//$'\t'/\\t}"
171+
s="${s//$'\r'/\\r}"
172+
printf '%s' "$s"
173+
}
174+
163175
check_file() { [[ -f "$1" ]] && echo "$2" || echo "$2"; }
164176
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo "$2" || echo "$2"; }
165177

scripts/bash/create-new-feature.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ clean_branch_name() {
162162
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
163163
}
164164

165+
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
166+
json_escape() {
167+
local s="$1"
168+
s="${s//\\/\\\\}"
169+
s="${s//\"/\\\"}"
170+
s="${s//$'\n'/\\n}"
171+
s="${s//$'\t'/\\t}"
172+
s="${s//$'\r'/\\r}"
173+
printf '%s' "$s"
174+
}
175+
165176
# Resolve repository root. Prefer git information when available, but fall back
166177
# to searching for repository markers so the workflow still functions in repositories that
167178
# were initialised with --no-git.
@@ -304,18 +315,18 @@ if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"
304315
printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2
305316

306317
if $JSON_MODE; then
307-
if has_jq; then
318+
if command -v jq >/dev/null 2>&1; then
308319
jq -cn \
309320
--arg branch_name "$BRANCH_NAME" \
310321
--arg spec_file "$SPEC_FILE" \
311322
--arg feature_num "$FEATURE_NUM" \
312323
'{BRANCH_NAME:$branch_name,SPEC_FILE:$spec_file,FEATURE_NUM:$feature_num}'
313324
else
314-
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM"
325+
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$(json_escape "$BRANCH_NAME")" "$(json_escape "$SPEC_FILE")" "$(json_escape "$FEATURE_NUM")"
315326
fi
316327
else
317328
echo "BRANCH_NAME: $BRANCH_NAME"
318329
echo "SPEC_FILE: $SPEC_FILE"
319330
echo "FEATURE_NUM: $FEATURE_NUM"
320-
echo "# To persist in your shell: export SPECIFY_FEATURE=$BRANCH_NAME"
331+
printf '# To persist in your shell: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME"
321332
fi

scripts/bash/setup-plan.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ if $JSON_MODE; then
6161
'{FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,SPECS_DIR:$specs_dir,BRANCH:$branch,HAS_GIT:$has_git}'
6262
else
6363
printf '{"FEATURE_SPEC":"%s","IMPL_PLAN":"%s","SPECS_DIR":"%s","BRANCH":"%s","HAS_GIT":"%s"}\n' \
64-
"$FEATURE_SPEC" "$IMPL_PLAN" "$FEATURE_DIR" "$CURRENT_BRANCH" "$HAS_GIT"
64+
"$(json_escape "$FEATURE_SPEC")" "$(json_escape "$IMPL_PLAN")" "$(json_escape "$FEATURE_DIR")" "$(json_escape "$CURRENT_BRANCH")" "$(json_escape "$HAS_GIT")"
6565
fi
6666
else
6767
echo "FEATURE_SPEC: $FEATURE_SPEC"

scripts/bash/update-agent-context.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -688,19 +688,23 @@ update_specific_agent() {
688688

689689
update_all_existing_agents() {
690690
local found_agent=false
691-
declare -A updated_paths
691+
_updated_paths=()
692692

693693
# Helper: skip non-existent files and files already updated (dedup by
694694
# realpath so that variables pointing to the same file — e.g. AMP_FILE,
695695
# KIRO_FILE, BOB_FILE all resolving to AGENTS_FILE — are only written once).
696+
# Uses a linear array instead of associative array for bash 3.2 compatibility.
696697
update_if_new() {
697698
local file="$1" name="$2"
698699
[[ -f "$file" ]] || return 0
699700
local real_path
700701
real_path=$(realpath "$file" 2>/dev/null || echo "$file")
701-
[[ -z "${updated_paths[$real_path]+x}" ]] || return 0
702+
local p
703+
for p in ${_updated_paths[@]+"${_updated_paths[@]}"}; do
704+
[[ "$p" == "$real_path" ]] && return 0
705+
done
702706
update_agent_file "$file" "$name" || return 1
703-
updated_paths["$real_path"]=1
707+
_updated_paths+=("$real_path")
704708
found_agent=true
705709
}
706710

0 commit comments

Comments
 (0)