Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ jobs:
bash "$t"
done

- name: retrofit-hooks fixture tests
run: |
set -e
for t in test/retrofit-hooks/test-*.sh; do
bash "$t"
done

- name: schema-hygiene fixture tests
run: |
set -e
for t in test/schema-hygiene/test-*.sh; do
bash "$t"
done

- name: Install jq for availability-check tests
run: sudo apt-get install -y jq

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to this project. Format: [Keep a Changelog](https://keepacha

## Unreleased

- **fix(retrofit): union hook groups by command within a matcher so a standalone→bundled membership change stops double-firing (dogfood F1 + F3).** The 2026-05-30 dogfood (upgrading an adjacent Next.js project from `cc-configure 2.6.0` to post-2.6.0 HEAD) hit a HIGH-severity defect PR #61 had explicitly scoped out: `deep_merge_settings` merged hook groups per event with whole-dict `==` dedup (`_merge_unique_list`), so when the configurator moves a hook between releases from a **standalone** matcher group to a **bundled** one — exactly what the post-2.6.0 `safety` change does, splitting a lone `block-dangerous-bash` Bash group into `[block-dangerous-bash, check-package-availability]` — the two group dicts aren't equal, the new bundled group is **appended**, and the user's `.claude/settings.json` ends with two `Bash` groups both containing `block-dangerous-bash`, which then **fires twice on every Bash call**. PR #61's CHANGELOG called this "rare … out of scope"; the dogfood revises that — adding a hook to an existing matcher group is the *normal* way a module grows, so ~100% of safety-module users upgrading 2.6.0→latest inherit a double-firing dangerous-bash guard. **Fix:** new `_merge_hook_groups(existing, new)` replaces the whole-group dedup on the hooks path — it keys by `matcher` and unions inner `hooks[]` by `command`, appending a configurator command only when it's absent from *every* existing group sharing that matcher (so the standalone+bundled pair collapses to one group, each command wired once). User customizations still survive untouched (a different command, a different matcher, or a tweaked-timeout copy under the same matcher is preserved — PR #61's `test-user-customizations-preserved` still passes), and the old N+1 self-heal still collapses exact whole-group duplicates. The `[ MERGED ]` summary gains an "N hook command(s)" counter. **F3 belt-and-suspenders:** `check_settings_validates` gains a third check (new `_find_duplicate_hook_commands`) that flags any command wired more than once under the same `(event, matcher)` — keyed by matcher so the legitimate "same command under different matchers" case is *not* flagged — emitting a `[ SETTINGS WARNINGS ]` line. The merge prevents the corruption on every shipped upgrade path; F3 catches a residual already-corrupted `settings.json` (e.g. a user who tracked unreleased HEAD before this fix; that bundled-safety-patch was never tagged, so no released upgrade produces it). New TDD fixtures: `test/retrofit-hooks/test-standalone-bundled-merge.sh` (the exact dogfood shape; fails against the old `_merge_unique_list`) and `test/schema-hygiene/test-duplicate-hook-commands.sh` (same-matcher dup flagged, cross-matcher allowed, within-group caught, clean + no-hooks-key pass). **CI gap also fixed:** `test/retrofit-hooks/` and `test/schema-hygiene/` (created by PRs #60/#61) were never wired into `.github/workflows/check.yml` — they only ran locally; both directories are now added as workflow steps, so all seven prior fixtures plus the two new ones run in CI for the first time. Static `configure.py --check` clean; adversarially reviewed (non-list `hooks[]` append-path crash hardened with a coercion guard for parity with the read-side helpers).
- **fix(safety): close `block-dangerous-bash` trailing-slash + `$HOME` bypass; refresh CC compat survey to 2.1.154.** The PreToolUse guard `templates/safety/hooks/block-dangerous-bash.sh` had the same blind spot Claude Code itself fixed in 2.1.154 ("`rm -rf $HOME` not blocked as a dangerous path when `HOME` has a trailing slash"): its `~($|\s)` and `/($|\s)` anchors stopped matching the instant a trailing slash followed, and the `$HOME` variable form was never matched at all — so `rm -rf ~/`, `rm -rf //`, `rm -rf $HOME`, `rm -rf $HOME/`, and `rm -rf "$HOME"/` all sailed through while bare `rm -rf ~` and `rm -rf /` were blocked. Widened three patterns: `/+` (also catches `//`), `~/?` (also catches `~/`), and a new `"?\$HOME"?/?` pattern covering the bare, quoted, and trailing-slash variable forms — all still anchored on end-or-space so subdirectory deletes (`rm -rf ~/myproject/node_modules`, `rm -rf $HOME/tmp`, `rm -rf ./build`) stay allowed. New `test/safety/` group with `test-block-dangerous-bash.sh` (7 should-block + 4 should-allow cases, TDD red→green — the guard previously had no behavioral test) wired into `.github/workflows/check.yml` as a `Safety hook fixture tests` step. Also refreshes the `CLAUDE_CODE_COMPAT` rationale comment in `config_schema.py` for the 2.1.151–154 survey (2026-05-28): flags `CLAUDE_CODE_OPUS_4_6_FAST_MODE_OVERRIDE` as deprecated/removed-2026-06-01 (CC 2.1.154); reclassifies hook-payload fields `terminalSequence` (CC 2.1.141) and `background_tasks`/`session_crons` (CC 2.1.145) as doc-governed/not-SchemaStore-gated and ready to ship; records that 2.1.151–154 added no new in-territory `settings.json` keys and that SchemaStore sync PR #5723 remains open, so `tested_up_to` stays pinned at 2.1.150. No `tested_up_to` bump (no-lone-bumps rule); the guard fix is independent of any schema gate.
- **feat(safety): package-availability gate + OS/tool-version status-line chip.** New PreToolUse Bash hook `safety/hooks/check-package-availability.sh` hard-denies `apt|apt-get|brew|dnf|yum|pacman|apk install` commands when the target packages aren't in any configured repo. Structured denial message lists missing packages, related siblings found via per-PM regex search (up to 8), the detected installed major version of the same family (e.g., `PostgreSQL 17.4` when blocking `postgresql-18`), three concrete next-step options, and a stale-cache warning when `/var/lib/apt/lists/` is older than 7 days. Shared bash libs land at `templates/safety/hooks/_lib/` (`availability_check.sh`, `detect_tool_versions.sh`) — the first reusable across hooks, the second consumed by the status line. UI module's `templates/ui/statusline.sh` now appends an OS+tool-version chip (`deb13 · pg17 · node20 · py3.13 · docker27`) by sourcing `detect_tool_versions.sh` per render; new `ui.no_version_chip` flag emits `CC_STATUSLINE_NO_VERSION_CHIP=1` for opt-out. Hook is fail-open by design: composite shell expressions (`|`, `&&`, `;`, `$()`, backticks), globs, brace expansion, shell variables, file installs (`./local.deb`, `*.rpm`, `*.apk`), missing `jq`, slow probes (>3s `timeout`), and any internal error (ERR trap) all bail with stderr note and exit 0. Per-probe time bound at 3s. Always-declared `pkgs`/`missing`/`pm` state lets `_log_decision` (JSONL writer to `.claude/logs/availability-check.log`) run safely from ERR trap before parsing. v1 covers OS package managers only — language PMs (pip/npm/cargo/gem/go) and download-URL freshness are tracked for follow-up PRs; URL freshness in particular has a high false-positive rate at the network level and is deferred until a semantic check exists. New `test/availability-check/` directory with 19 fixture tests (TDD-developed per the brainstormed spec/plan) covers the per-PM probes, the chip with/without stub fixtures, the JSON-parse decision matrix, every parser bailout, hook regressions (sudo + env stripping, value-taking flags like `-t bookworm-backports`, version pins, multi-pkg with mixed available+missing), error handling (jq missing, malformed JSON, stale cache), and JSONL logging conditionality on `.claude/logs/` presence. CI gains an `Install jq` step + `Availability-check hook tests` loop in `.github/workflows/check.yml`. Persona snapshots regenerated for all four personas that ship `safety/` (custom unchanged). Spec + plan in `docs/superpowers/specs/2026-05-27-package-availability-gate-design.md` and `docs/superpowers/plans/2026-05-27-package-availability-gate.md` (gitignored, local-only per PR #50).
- **feat(license): relicense from MIT to AGPL-3.0 with discipline-skills MIT carve-out.** The configurator's own code (configure.py, config_schema.py, all original templates, hooks, docs, tests, examples) switches to GNU Affero General Public License v3.0 to close the SaaS loophole that lets hyperscalers fork permissive copyleft projects into closed managed services without contributing back (the "MongoDB on AWS" pattern that drove MongoDB→SSPL, Elastic→ELv2/SSPL, and HelixDB's 2025-05-13 GPL→AGPL switch — the proximate model for this change). Network-served modifications now trigger AGPL-3.0 source-distribution requirements. **Carve-out:** `templates/discipline-skills/` keeps its MIT License (© 2025 Jesse Vincent, forked from obra/superpowers v5.1.0) — the MIT terms travel with those files when users install the module into their projects; the surrounding configurator code is AGPL-3.0; MIT is compatible with AGPL aggregation. New root `NOTICE` documents the bundled-license breakdown. README `## License` section rewritten to explain the change + carve-out + irrevocability of past MIT releases. AGPL-3.0 added to the user-facing license picker in `config_schema.py` (between Apache-2.0 and GPL-3.0) so users can select it for their own projects. `test/discipline-skills/test-license-attribution.sh` continues to pass (validates the MIT carve-out, not the root license). Past tagged releases remain MIT under their original terms; AGPL-3.0 applies to all subsequent commits.
Expand Down
Loading