cds-reapply-patches — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cds-reapply-patches (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<purpose> After a GSD update wipes and reinstalls files, this command merges user's previously saved local modifications back into the new version. Uses three-way comparison (pristine baseline, user-modified backup, newly installed version) to reliably distinguish user customizations from version drift.
Critical invariant: Every file in gsd-local-patches/ was backed up because the installer's hash comparison detected it was modified. The workflow must NEVER conclude "no custom content" for any backed-up file — that is a logical contradiction. When in doubt, classify as CONFLICT requiring user review, not SKIP. </purpose>
<process>
Check for local patches directory:
expand_home() {
case "$1" in
"~/"*) printf '%s/%s\n' "$HOME" "${1#~/}" ;;
*) printf '%s\n' "$1" ;;
esac
}
PATCHES_DIR=""
# Env overrides first — covers custom config directories used with --config-dir
if [ -n "$KILO_CONFIG_DIR" ]; then
candidate="$(expand_home "$KILO_CONFIG_DIR")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
elif [ -n "$KILO_CONFIG" ]; then
candidate="$(dirname "$(expand_home "$KILO_CONFIG")")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
elif [ -n "$XDG_CONFIG_HOME" ]; then
candidate="$(expand_home "$XDG_CONFIG_HOME")/kilo/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
fi
if [ -z "$PATCHES_DIR" ] && [ -n "$OPENCODE_CONFIG_DIR" ]; then
candidate="$(expand_home "$OPENCODE_CONFIG_DIR")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
elif [ -z "$PATCHES_DIR" ] && [ -n "$OPENCODE_CONFIG" ]; then
candidate="$(dirname "$(expand_home "$OPENCODE_CONFIG")")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
elif [ -z "$PATCHES_DIR" ] && [ -n "$XDG_CONFIG_HOME" ]; then
candidate="$(expand_home "$XDG_CONFIG_HOME")/opencode/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
fi
if [ -z "$PATCHES_DIR" ] && [ -n "$GEMINI_CONFIG_DIR" ]; then
candidate="$(expand_home "$GEMINI_CONFIG_DIR")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
fi
if [ -z "$PATCHES_DIR" ] && [ -n "$CODEX_HOME" ]; then
candidate="$(expand_home "$CODEX_HOME")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
fi
if [ -z "$PATCHES_DIR" ] && [ -n "$CLAUDE_CONFIG_DIR" ]; then
candidate="$(expand_home "$CLAUDE_CONFIG_DIR")/cds-local-patches"
if [ -d "$candidate" ]; then
PATCHES_DIR="$candidate"
fi
fi
# Global install — detect runtime config directory defaults
if [ -z "$PATCHES_DIR" ]; then
if [ -d "$HOME/.config/kilo/cds-local-patches" ]; then
PATCHES_DIR="$HOME/.config/kilo/cds-local-patches"
elif [ -d "$HOME/.config/opencode/cds-local-patches" ]; then
PATCHES_DIR="$HOME/.config/opencode/cds-local-patches"
elif [ -d "$HOME/.opencode/cds-local-patches" ]; then
PATCHES_DIR="$HOME/.opencode/cds-local-patches"
elif [ -d "$HOME/.gemini/cds-local-patches" ]; then
PATCHES_DIR="$HOME/.gemini/cds-local-patches"
elif [ -d "$HOME/.codex/cds-local-patches" ]; then
PATCHES_DIR="$HOME/.codex/cds-local-patches"
else
PATCHES_DIR="$HOME/.claude/cds-local-patches"
fi
fi
# Local install fallback — check all runtime directories
if [ ! -d "$PATCHES_DIR" ]; then
for dir in .config/kilo .kilo .config/opencode .opencode .gemini .codex .claude; do
if [ -d "./$dir/cds-local-patches" ]; then
PATCHES_DIR="./$dir/cds-local-patches"
break
fi
done
fiRead backup-meta.json from the patches directory.
If no patches found:
No local patches found. Nothing to reapply.
Local patches are automatically saved when you run /cds-update
after modifying any GSD workflow, command, or agent files.Exit.
The quality of the merge depends on having a pristine baseline — the original unmodified version of each file from the pre-update GSD release. This enables three-way comparison:
gsd-local-patches/)Check for baseline sources in priority order:
If the config directory is a git repository:
CONFIG_DIR=$(dirname "$PATCHES_DIR")
if git -C "$CONFIG_DIR" rev-parse --git-dir >/dev/null 2>&1; then
HAS_GIT=true
fiWhen HAS_GIT=true, use git log to find the commit where GSD was originally installed (before user edits). For each file, the pristine baseline can be extracted with:
git -C "$CONFIG_DIR" log --diff-filter=A --format="%H" -- "{file_path}"This gives the commit that first added the file (the install commit). Extract the pristine version:
git -C "$CONFIG_DIR" show {install_commit}:{file_path}Check if a gsd-pristine/ directory exists alongside gsd-local-patches/:
PRISTINE_DIR="$CONFIG_DIR/cds-pristine"If it exists, the installer saved pristine copies at install time. Use these as the baseline.
If neither git history nor pristine snapshots are available, fall back to two-way comparison — but with strengthened heuristics (see Step 3).
## Local Patches to Reapply
**Backed up from:** v{from_version}
**Current version:** {read VERSION file}
**Files modified:** {count}
**Merge strategy:** {three-way (git) | three-way (pristine) | two-way (enhanced)}
| # | File | Status |
|---|------|--------|
| 1 | {file_path} | Pending |
| 2 | {file_path} | Pending |For each file in backup-meta.json:
gsd-local-patches/)gsd-pristine/)Compare the three versions to isolate changes:
Merge rules:
When no pristine baseline is available, use these strengthened heuristics:
CRITICAL RULE: Every file in this backup directory was explicitly detected as modified by the installer's SHA-256 hash comparison. "No custom content" is never a valid conclusion.
For each file: a. Read both versions completely b. Identify ALL differences, then classify each as:
/Users/xxx/.claude/ → $HOME/.claude/), variable additions (${GSD_WS}, ${AGENT_SKILLS_*}), error handling additions (|| true)c. If ANY differences remain after filtering out mechanical drift → those are user customizations. Merge them. d. If ALL differences appear to be mechanical drift → still flag as CONFLICT. The installer's hash check already proved this file was modified. Ask the user: "This file appears to only have path/variable differences. Were there intentional customizations?" Do NOT silently skip.
When the config directory is a git repo but the pristine install commit can't be found, use commit history to identify user changes:
# Find non-update commits that touched this file
git -C "$CONFIG_DIR" log --oneline --no-merges -- "{file_path}" | grep -v "gsd:update\|GSD update\|gsd-install"Each matching commit represents an intentional user modification. Use the commit messages and diffs to understand what was changed and why.
After writing each merged file, verify that user modifications survived the merge:
⚠ Potential dropped content in {file_path}:
- Missing hunk near line {N}: "{first_line_preview}..." ({line_count} lines)
- Backup available: {patches_dir}/{file_path}| file | hunk_id | signature_line | line_count | verified |
|---|---|---|---|---|
| {file_path} | {N} | {first_significant_line} | {count} | yes |
| {file_path} | {N} | {first_significant_line} | {count} | no |
hunk_id — sequential integer per file (1, 2, 3…)signature_line — first non-blank, non-comment line of the user-added sectionline_count — total lines in the hunkverified — yes if the signature_line is present in the merged output, no otherwiseMerged (verified) vs Merged (⚠ {N} hunks may be missing)Merged — user modifications applied cleanly (show summary of what was preserved)Conflict — user reviewed and chose resolutionIncorporated — user's modification was already adopted upstream (only valid when pristine baseline confirms this)Never report `Skipped — no custom content`. If a file is in the backup, it has custom content.
Before proceeding to cleanup, evaluate the Hunk Verification Table produced in Step 4.
If the Hunk Verification Table is absent (Step 4 did not produce it), STOP immediately and report to the user:
ERROR: Hunk Verification Table is missing. Post-merge verification was not completed.
Rerun /cds-reapply-patches to retry with full verification.If any row in the Hunk Verification Table shows `verified: no`, STOP and report to the user:
ERROR: {N} hunk(s) failed verification — content may have been dropped during merge.
Unverified hunks:
{file} hunk {hunk_id}: signature line "{signature_line}" not found in merged output
The backup is preserved at: {patches_dir}/{file}
Review the merged file manually, then either:
(a) Re-merge the missing content by hand, or
(b) Restore from backup: cp {patches_dir}/{file} {installed_path}Do not proceed to cleanup until the user confirms they have resolved all unverified hunks.
Only when all rows show `verified: yes` (or when all files had zero user-added hunks) may execution continue to Step 6.
Ask user:
gsd-local-patches/gsd-local-patches/ directory## Patches Reapplied
| # | File | Result | User Changes Preserved |
|---|------|--------|----------------------|
| 1 | {file_path} | Merged | Added step X, modified section Y |
| 2 | {file_path} | Incorporated | Already in upstream v{version} |
| 3 | {file_path} | Conflict resolved | User chose: keep custom section |
{count} file(s) updated. Your local modifications are active again.</process>
<success_criteria>
</success_criteria>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.