Skip to content

Commit ee9ea53

Browse files
Copilotintel352
andauthored
fix: address all 9 PR review feedback items
Agent-Logs-Url: https://github.com/GoCodeAlone/claude-superpowers/sessions/333f45fb-54f9-4471-a162-9d3bdd01cb0e Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
1 parent f96d5fb commit ee9ea53

7 files changed

Lines changed: 47 additions & 38 deletions

File tree

hooks/pre-compact-snapshot

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,6 @@ hook_input=$(cat || true)
2626
cwd_dir=$(printf '%s' "$hook_input" | jq -r '.cwd // empty' 2>/dev/null || true)
2727
[ -z "$cwd_dir" ] && cwd_dir="${PWD}"
2828

29-
# ── Portable sha256 (same helper as tests/plan-scope-check.sh) ───────────────
30-
sha256_file() {
31-
if command -v sha256sum >/dev/null 2>&1; then
32-
sha256sum "$1" 2>/dev/null | awk '{print $1}'
33-
elif command -v shasum >/dev/null 2>&1; then
34-
shasum -a 256 "$1" 2>/dev/null | awk '{print $1}'
35-
else
36-
echo "unavailable"
37-
fi
38-
}
39-
4029
# ── Find locked plans and collect their state ─────────────────────────────────
4130
plans_dir="${cwd_dir}/docs/plans"
4231
state_section=""
@@ -50,10 +39,12 @@ if [ -d "$plans_dir" ]; then
5039
# Extract Status line
5140
status_line=$(grep '\*\*Status:\*\*' "$plan" 2>/dev/null | tail -1 | sed 's/.*\*\*Status:\*\*[[:space:]]*//' || true)
5241

53-
# Hash of the lock file (the manifest hash stored at lock time)
42+
# Read the manifest hash stored inside the lock file (first non-comment,
43+
# non-blank line). This is the same value tests/plan-scope-check.sh
44+
# --verify-lock compares against the current manifest sha256.
5445
if [ -f "$lock_file" ]; then
55-
lock_hash=$(sha256_file "$lock_file")
56-
state_section="${state_section} ${plan_name}: ${status_line} (lock-file sha256: ${lock_hash})\n"
46+
manifest_hash=$(awk 'NF && !/^#/ {print; exit}' "$lock_file" 2>/dev/null || true)
47+
state_section="${state_section} ${plan_name}: ${status_line} (manifest-sha256: ${manifest_hash:-unknown})\n"
5748
else
5849
state_section="${state_section} ${plan_name}: ${status_line} (no lock file — not yet locked or lock file missing)\n"
5950
fi

hooks/pre-tool-scope-guard

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@ case "$tool_name" in
6868
# SUPERPOWERS_ALLOW_DEFAULT_BRANCH=1 git push origin main
6969
# env SUPERPOWERS_SCOPE_LOCK_WRITE=1 bash -c '...'
7070
# SUPERPOWERS_PLAN_LOCK_WRITE=1; git commit ... (before semicolon)
71-
if printf '%s' "$cmd" | grep -qE '(^|[;&|[:space:]])(export[[:space:]]+)?SUPERPOWERS_[A-Z_]+='; then
71+
# Not blocked (benign debugging/grepping):
72+
# echo "SUPERPOWERS_HOOKS_DISABLE=1"
73+
# grep 'SUPERPOWERS_.*=' file
74+
# Best-effort: strip single- and double-quoted strings before checking so
75+
# that mentions of SUPERPOWERS_* inside quoted arguments don't trigger a
76+
# false-positive block.
77+
cmd_no_quotes=$(printf '%s' "$cmd" | sed "s/\"[^\"]*\"//g; s/'[^']*'//g")
78+
if printf '%s' "$cmd_no_quotes" | grep -qE '(^|[;&|[:space:]])(export[[:space:]]+)?SUPERPOWERS_[A-Z_]+='; then
7279
block "Self-bypass attempt blocked: setting a SUPERPOWERS_* environment variable from inside a Bash tool call is not permitted. These variables control pipeline enforcement gates and may only be set by the human operator in their terminal environment before starting the agent session. The agent cannot grant itself permission to bypass its own constraints — that would make the gates meaningless."
7380
fi
7481

hooks/subagent-scope-guard

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ violations=""
4343

4444
# Working-tree and index modifications to protected files
4545
if command -v git >/dev/null 2>&1; then
46-
(
47-
cd "$cwd_dir" 2>/dev/null || exit 0
46+
_saved_pwd="${PWD}"
47+
if cd "$cwd_dir" 2>/dev/null; then
4848

4949
# Uncommitted changes to scope-lock files
5050
scope_lock_dirty=$(git status --porcelain 2>/dev/null \
@@ -86,7 +86,9 @@ if command -v git >/dev/null 2>&1; then
8686
done <<< "$plans_in_commit"
8787
fi
8888
fi
89-
)
89+
90+
cd "$_saved_pwd"
91+
fi
9092
fi
9193

9294
[ -z "$violations" ] && exit 0

skills/scope-lock/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ The unlock path is intentionally heavyweight. Cheap unlock = no lock at all.
108108
- Commit both files in the same commit: `chore: lock scope for <feature> (alignment passed)`.
109109

110110
**`subagent-driven-development` (per-task checkpoint):**
111-
- Before dispatching the next task, run `tests/plan-scope-check.sh --plan <plan-path>` to verify (a) the plan's manifest hash still matches `<plan-path>.scope-lock`, (b) every commit on the feature branch traces to a task in the manifest, (c) no manifest task is missing.
111+
- Before dispatching the next task, run `tests/plan-scope-check.sh --verify-lock <plan-path>` to verify (a) the plan's manifest hash still matches `<plan-path>.scope-lock`, (b) every commit on the feature branch traces to a task in the manifest, (c) no manifest task is missing.
112112
- On any FAIL, stop dispatching new work; surface the discrepancy to the user.
113113
- After all tasks complete, run the same check before invoking `finishing-a-development-branch`.
114114

tests/plan-scope-check.sh

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
# <path>.scope-lock (only meaningful after the plan is
1414
# in Locked status).
1515
# --against-branch <plan> Verify the actual git branch layout matches the
16-
# PR Grouping table: every commit since the merge-base
17-
# with the plan's base branch is reachable from a
18-
# branch listed in the table; every branch in the
16+
# PR Grouping table: every branch listed in the
1917
# table exists locally or on origin.
2018
#
2119
# Multiple modes can be combined. With no flags, runs --plan on every plan in
@@ -66,11 +64,10 @@ sha256_stdin() {
6664
}
6765

6866
# Check the manifest is well-formed. Args: plan path. Echoes problems to stdout.
69-
# Legacy plans (no manifest section AND no `# scope-manifest: required` marker
70-
# in a hidden HTML comment) are skipped — only plans that opt into the format
71-
# are enforced. New plans created by writing-plans always include the section,
72-
# so this only matters for grandfathering historical plans pre-dating the
73-
# scope-lock skill.
67+
# Legacy plans (no manifest section) are skipped — only plans that opt into the
68+
# format are enforced. New plans created by writing-plans always include the
69+
# section, so this only matters for grandfathering historical plans pre-dating
70+
# the scope-lock skill.
7471
check_manifest_wellformed() {
7572
local plan="$1"
7673
local manifest

tests/skill-activation-audit.sh

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,24 @@ if [ ! -r "$STATE_FILE" ]; then
6767
fi
6868

6969
# Pipeline gates we expect for an autonomous run, in order. The pipeline
70-
# is the canonical chain documented in skills/using-superpowers/SKILL.md.
70+
# is the canonical chain documented in skills/using-superpowers/SKILL.md:
71+
# brainstorming → adversarial-design-review (design) → writing-plans →
72+
# adversarial-design-review (plan) → alignment-check → scope-lock →
73+
# subagent-driven-development → finishing-a-development-branch →
74+
# pr-monitoring → post-merge-retrospective
75+
# Note: adversarial-design-review appears twice (design and plan phases);
76+
# this list de-dupes it — the audit reports the count seen so gaps can be
77+
# identified but cannot distinguish the two phases without --phase= args.
7178
PIPELINE_GATES=(
7279
brainstorming
7380
adversarial-design-review
7481
writing-plans
7582
alignment-check
83+
scope-lock
7684
subagent-driven-development
7785
finishing-a-development-branch
7886
pr-monitoring
87+
post-merge-retrospective
7988
)
8089

8190
# Optional gates — present only when conditions trigger them. Reported
@@ -112,10 +121,10 @@ extract_skills() {
112121

113122
extract_agents() {
114123
if command -v jq >/dev/null 2>&1; then
115-
jq -r 'select(.tool=="Agent" or .tool=="Task") | .detail' "$STATE_FILE" 2>/dev/null \
124+
jq -r 'select(.tool=="Agent" or (.tool | type=="string" and startswith("Task"))) | .detail' "$STATE_FILE" 2>/dev/null \
116125
| sed -nE 's/.*agent=([A-Za-z0-9_-]+).*/\1/p'
117126
else
118-
grep -E '"tool":"(Agent|Task)"' "$STATE_FILE" 2>/dev/null \
127+
grep -E '"tool":"(Agent|Task[^"]*)"' "$STATE_FILE" 2>/dev/null \
119128
| sed -nE 's/.*agent=([A-Za-z0-9_-]+).*/\1/p'
120129
fi
121130
}

tests/skill-cross-refs.sh

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ tmp_failures="$(mktemp)" || { echo "ERROR: mktemp failed" >&2; exit 3; }
3131
trap 'rm -f "$tmp_failures"' EXIT
3232

3333
# Build the set of known skill names and agent names from the filesystem.
34-
known_skills="$(find skills -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -u)"
35-
known_agents="$(find agents -mindepth 1 -maxdepth 1 -type f -name '*.md' -printf '%f\n' | sed -E 's|\.md$||' | sort -u)"
34+
known_skills="$(find skills -mindepth 1 -maxdepth 1 -type d | sed -E 's|.*/||' | sort -u)"
35+
known_agents="$(find agents -mindepth 1 -maxdepth 1 -type f -name '*.md' | sed -E 's|.*/||; s|\.md$||' | sort -u)"
3636

3737
# Helper: is the name a known skill or agent?
3838
is_known_target() {
@@ -68,12 +68,14 @@ strip_fences() {
6868

6969
# Files to scan. Exclude *creation-log* / changelog-style files where the
7070
# point is to record historical names that no longer exist.
71-
mapfile -t scan_files < <(find skills agents -type f -name '*.md' \
72-
! -iname 'CREATION-LOG.md' | sort)
71+
# Use a newline-separated string for portability (no mapfile / Bash 4+).
72+
scan_files_list="$(find skills agents -type f -name '*.md' \
73+
! -iname 'CREATION-LOG.md' | sort)"
7374

7475
# --- 1. Skill / agent references ----------------------------------------
7576

76-
for f in "${scan_files[@]}"; do
77+
while IFS= read -r f; do
78+
[ -z "$f" ] && continue
7779
annotated="$(strip_fences "$f")"
7880

7981
# Pattern 1: bare `<slug>/SKILL.md` references
@@ -118,7 +120,7 @@ for f in "${scan_files[@]}"; do
118120
fi
119121
done
120122
done < <(printf '%s\n' "$annotated" | grep -E 'superpowers:[a-z][a-z0-9-]+' || true)
121-
done
123+
done <<< "$scan_files_list"
122124

123125
# --- 2. Step references --------------------------------------------------
124126

@@ -141,7 +143,8 @@ has_step() {
141143
|| grep -qE "Step[[:space:]]+${step}[[:space:]]*[:.]" "$file"
142144
}
143145

144-
for f in "${scan_files[@]}"; do
146+
while IFS= read -r f; do
147+
[ -z "$f" ] && continue
145148
annotated="$(strip_fences "$f")"
146149

147150
while IFS=: read -r line_no line; do
@@ -162,7 +165,7 @@ for f in "${scan_files[@]}"; do
162165
| sed -E "s/^([a-z][a-z0-9-]+)[a-z']*[[:space:]]+Step[[:space:]]+([0-9]+[a-z]?).*/\1 \2/")
163166
done < <(printf '%s\n' "$annotated" \
164167
| grep -E "[a-z][a-z0-9-]+[a-z']*[[:space:]]+Step[[:space:]]+[0-9]+[a-z]?" || true)
165-
done
168+
done <<< "$scan_files_list"
166169

167170
# --- Report --------------------------------------------------------------
168171

0 commit comments

Comments
 (0)