* fix(scripts): encode residual control chars as \uXXXX instead of stripping
json_escape() was silently deleting control characters (U+0000-U+001F)
that were not individually handled (\n, \t, \r, \b, \f). Per RFC 8259,
these must be encoded as \uXXXX sequences to preserve data integrity.
Replace the tr -d strip with a char-by-char loop that emits proper
\uXXXX escapes for any remaining control characters.
* fix(scripts): address Copilot review on json_escape control char loop
- Set LC_ALL=C for the entire loop (not just printf) so that ${#s} and
${s:$i:1} operate on bytes deterministically across locales
- Fix comment: U+0000 (NUL) cannot exist in bash strings, range is
U+0001-U+001F; adjust code guard accordingly (code >= 1)
- Emit directly to stdout instead of accumulating in a variable,
avoiding quadratic string concatenation on longer inputs
* perf(scripts): use printf -v to avoid subshell in json_escape loop
Replace code=$(printf ...) with printf -v code to assign the character
code without spawning a subshell on every byte, reducing overhead for
longer inputs.
* fix(scripts): harden bash scripts with escape, compat, and cleanup fixes
- common.sh: complete RFC 8259 JSON escape (\b, \f, strip control chars)
- common.sh: distinguish python3 success-empty vs failure in resolve_template
- check-prerequisites.sh: escape doc names through json_escape in fallback path
- create-new-feature.sh: remove duplicate json_escape (already in common.sh)
- create-new-feature.sh: warn on stderr when spec template is not found
- update-agent-context.sh: move nested function to top-level for bash 3.2 compat
* fix(scripts): explicit resolve_template return code and best-effort agent updates
- common.sh: resolve_template now returns 1 when no template is found,
making the "not found" case explicit instead of relying on empty stdout
- setup-plan.sh, create-new-feature.sh: add || true to resolve_template
calls so set -e does not abort on missing templates (non-fatal)
- update-agent-context.sh: accumulate errors in update_all_existing_agents
instead of silently discarding them — all agents are attempted and the
composite result is returned, matching the PowerShell equivalent behavior
* style(scripts): add clarifying comment in resolve_template preset branch
* fix(scripts): wrap python3 call in if-condition to prevent set -e abort
Move the python3 command substitution in resolve_template into an
if-condition so that a non-zero exit (e.g. invalid .registry JSON)
does not abort the function under set -e. The fallback directory
scan now executes as intended regardless of caller errexit settings.
* fix(scripts): track agent file existence before update and avoid top-level globals
- _update_if_new now records the path and sets _found_agent before calling
update_agent_file, so that failures do not cause duplicate attempts on
aliased paths (AMP/KIRO/BOB -> AGENTS_FILE) or false "no agent files
found" fallback triggers
- Remove top-level initialisation of _updated_paths and _found_agent;
they are now created exclusively inside update_all_existing_agents,
keeping the script side-effect free when sourced
- Replace eval of unquoted get_feature_paths output with safe pattern:
capture into variable, check return code, then eval quoted result
- Use printf '%q' in get_feature_paths to safely emit shell assignments,
preventing injection via paths containing quotes or metacharacters
- Add json_escape() helper for printf JSON fallback paths, handling
backslash, double-quote, and control characters when jq is unavailable
- Use jq -cn for safe JSON construction with proper escaping when
available, with printf + json_escape() fallback
- Replace declare -A (bash 4+) with indexed array for bash 3.2
compatibility (macOS default)
- Use inline command -v jq check in create-new-feature.sh since it
does not source common.sh
- Guard trap cleanup against re-entrant invocation by disarming traps
at entry
- Use printf '%q' for shell-escaped branch names in user-facing output
- Return failure instead of silently returning wrong path on ambiguous
spec directory matches
- Deduplicate agent file updates via realpath to prevent multiple writes
to the same file (e.g. AGENTS.md aliased by multiple variables)