agentic-sdlc-wizard 1.37.0 → 1.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@
13
13
  "name": "sdlc-wizard",
14
14
  "source": ".",
15
15
  "description": "SDLC enforcement for AI agents — TDD, planning, self-review, CI shepherd",
16
- "version": "1.37.0",
16
+ "version": "1.37.1",
17
17
  "author": {
18
18
  "name": "Stefan Ayala"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdlc-wizard",
3
- "version": "1.37.0",
3
+ "version": "1.37.1",
4
4
  "description": "SDLC enforcement for AI agents — TDD, planning, self-review, CI shepherd",
5
5
  "author": {
6
6
  "name": "Stefan Ayala",
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to the SDLC Wizard.
4
4
 
5
5
  > **Note:** This changelog is for humans to read. Don't manually apply these changes - just run the wizard ("Check for SDLC wizard updates") and it handles everything automatically.
6
6
 
7
+ ## [1.37.1] - 2026-04-24
8
+
9
+ ### Fixed
10
+
11
+ - **Dual-channel hook 2× print** (token-bloat audit, ROADMAP item 8, PR #241). When both the project's `.claude/settings.json` AND a locally-installed wizard plugin (`~/.claude/plugins-local/` or `~/.claude/plugins/cache/`) registered the same hook, both fired per event — `SDLC BASELINE` block printed twice per `UserPromptSubmit`, ~300 tokens doubled per prompt. Fix: `dedupe_plugin_or_project()` helper in `hooks/_find-sdlc-root.sh`. Plugin invocation yields if project also registers the same hook by name (project always wins). Wired into all 5 hooks (sdlc-prompt-check, instructions-loaded-check, tdd-pretool-check, model-effort-check, precompact-seam-check). Consumer plugin-only installs still fire normally. Codex 2-round: 100/100 CERTIFIED. 9 new dedupe tests + 1 stale-fixture fix (test_instructions_hook_cwd_walkup now reads current version dynamically from package.json so it doesn't drift past the staleness-nudge threshold on each release).
12
+
7
13
  ## [1.37.0] - 2026-04-24
8
14
 
9
15
  ### Changed
@@ -2715,7 +2715,7 @@ If deployment fails or post-deploy verification catches issues:
2715
2715
 
2716
2716
  **SDLC.md:**
2717
2717
  ```markdown
2718
- <!-- SDLC Wizard Version: 1.37.0 -->
2718
+ <!-- SDLC Wizard Version: 1.37.1 -->
2719
2719
  <!-- Setup Date: [DATE] -->
2720
2720
  <!-- Completed Steps: step-0.1, step-0.2, step-0.4, step-1, step-2, step-3, step-4, step-5, step-6, step-7, step-8, step-9 -->
2721
2721
  <!-- Git Workflow: [PRs or Solo] -->
@@ -3777,7 +3777,7 @@ Walk through updates? (y/n)
3777
3777
  Store wizard state in `SDLC.md` as metadata comments (invisible to readers, parseable by Claude):
3778
3778
 
3779
3779
  ```markdown
3780
- <!-- SDLC Wizard Version: 1.37.0 -->
3780
+ <!-- SDLC Wizard Version: 1.37.1 -->
3781
3781
  <!-- Setup Date: 2026-01-24 -->
3782
3782
  <!-- Completed Steps: step-0.1, step-0.2, step-1, step-2, step-3, step-4, step-5, step-6, step-7, step-8, step-9 -->
3783
3783
  <!-- Git Workflow: PRs -->
@@ -34,3 +34,62 @@ find_partial_sdlc_root() {
34
34
  done
35
35
  return 1
36
36
  }
37
+
38
+ # dedupe_plugin_or_project — token-bloat fix.
39
+ # When a hook is registered via BOTH the project's `.claude/settings.json` AND
40
+ # a locally-installed wizard plugin (e.g., maintainer dogfooding the wizard
41
+ # while also having `~/.claude/plugins-local/sdlc-wizard-wrap/`), the same
42
+ # script fires twice per event = 2× tokens per prompt, 2× hook output noise.
43
+ #
44
+ # Resolution: plugin invocation yields if the project also registers the
45
+ # same hook by name. Project registration always wins (user-explicit).
46
+ # Consumer plugin-only installs (no project settings.json) still fire normally.
47
+ #
48
+ # Plugin path heuristic: $0 contains "/plugins-local/" or "/plugins/cache/".
49
+ #
50
+ # Usage:
51
+ # source _find-sdlc-root.sh
52
+ # dedupe_plugin_or_project || exit 0 # plugin yields when project registered
53
+ #
54
+ # Args (optional, for tests): $1 script_path, $2 project_dir
55
+ # Returns: 0 = proceed, 1 = yield (caller should exit 0).
56
+ #
57
+ # Codex review hardening (DEDUPE-001/002):
58
+ # - Uses parameter expansion (${path##*/}) instead of `basename` — survives
59
+ # PATH-restricted environments. Falsely emitting `basename: command not found`
60
+ # would corrupt the rc=1 (yield) signal.
61
+ # - Matches the script name only inside a `"command"` JSON field, not anywhere
62
+ # in the settings file. Otherwise a basename mentioned in `permissions.allow`
63
+ # or a comment would falsely trigger yield (plugin would skip when project
64
+ # never actually registers the hook).
65
+ dedupe_plugin_or_project() {
66
+ local script_path="${1:-${BASH_SOURCE[1]:-$0}}"
67
+ local project_dir="${2:-${CLAUDE_PROJECT_DIR:-.}}"
68
+
69
+ case "$script_path" in
70
+ */plugins-local/*|*/plugins/cache/*)
71
+ local proj_settings="$project_dir/.claude/settings.json"
72
+ [ -f "$proj_settings" ] || return 0
73
+
74
+ # Parameter-expansion basename: ${path##*/} strips up to last /.
75
+ # If no / in path, returns path itself (defensive — caller passed
76
+ # bare filename).
77
+ local script_name="${script_path##*/}"
78
+ [ -n "$script_name" ] || return 0
79
+
80
+ # Match only inside a "command" JSON registration so a basename
81
+ # appearing in permissions.allow / comments / unrelated sections
82
+ # doesn't falsely yield. Pattern: `"command"` followed by colon,
83
+ # any whitespace, optional quotes, anything, then the basename.
84
+ # Example matches:
85
+ # "command": "$CLAUDE_PROJECT_DIR/hooks/sdlc-prompt-check.sh"
86
+ # "command":"hooks/sdlc-prompt-check.sh"
87
+ # Does NOT match:
88
+ # "Bash(./hooks/sdlc-prompt-check.sh *)" (in permissions.allow)
89
+ if grep -qE '"command"[[:space:]]*:[[:space:]]*"[^"]*'"$script_name"'"' "$proj_settings" 2>/dev/null; then
90
+ return 1 # yield — project will fire its own copy
91
+ fi
92
+ ;;
93
+ esac
94
+ return 0 # proceed
95
+ }
@@ -8,6 +8,9 @@
8
8
  HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
9
  source "$HOOK_DIR/_find-sdlc-root.sh"
10
10
 
11
+ # Token-bloat fix: when both project + plugin register this hook, plugin yields.
12
+ dedupe_plugin_or_project "${BASH_SOURCE[0]}" || exit 0
13
+
11
14
  # CWD walk-up finds nearest SDLC project (#173: silent exit for non-SDLC dirs)
12
15
  if find_sdlc_root; then
13
16
  PROJECT_DIR="$SDLC_ROOT"
@@ -15,6 +15,13 @@
15
15
 
16
16
  RECOMMENDED_MODEL="opus[1m]"
17
17
 
18
+ # Token-bloat fix: when both project + plugin register this hook, plugin yields.
19
+ HOOK_DIR="${BASH_SOURCE[0]%/*}"
20
+ [ "$HOOK_DIR" = "${BASH_SOURCE[0]}" ] && HOOK_DIR="."
21
+ # shellcheck disable=SC1091
22
+ source "$HOOK_DIR/_find-sdlc-root.sh"
23
+ dedupe_plugin_or_project "${BASH_SOURCE[0]}" || { cat > /dev/null; exit 0; }
24
+
18
25
  # Drain stdin (SessionStart sends JSON but model field isn't in it)
19
26
  cat > /dev/null
20
27
 
@@ -14,6 +14,15 @@
14
14
  # → finish in-progress git operation first
15
15
  # Allow otherwise.
16
16
 
17
+ # Token-bloat fix: when both project + plugin register this hook, plugin yields.
18
+ # Use parameter expansion (not `dirname`) so the PATH-restricted gh-missing test
19
+ # still works — bash builtin `${var%/*}` is always available.
20
+ HOOK_DIR="${BASH_SOURCE[0]%/*}"
21
+ [ "$HOOK_DIR" = "${BASH_SOURCE[0]}" ] && HOOK_DIR="."
22
+ # shellcheck disable=SC1091
23
+ source "$HOOK_DIR/_find-sdlc-root.sh"
24
+ dedupe_plugin_or_project "${BASH_SOURCE[0]}" || { [ ! -t 0 ] && cat > /dev/null; exit 0; }
25
+
17
26
  [ ! -t 0 ] && INPUT=$(cat) || INPUT=""
18
27
 
19
28
  # Determine project root: prefer $CLAUDE_PROJECT_DIR, fall back to cwd
@@ -6,6 +6,12 @@
6
6
  HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
7
  source "$HOOK_DIR/_find-sdlc-root.sh"
8
8
 
9
+ # Token-bloat fix: when both project + plugin register this hook, plugin yields.
10
+ # Prevents 2× SDLC BASELINE print per UserPromptSubmit (~300 tokens doubled).
11
+ # Pass real script path explicitly (not $0) so the dedupe heuristic recognizes
12
+ # plugin paths even when the script is sourced or invoked via aliases.
13
+ dedupe_plugin_or_project "${BASH_SOURCE[0]}" || exit 0
14
+
9
15
  # CWD walk-up finds nearest SDLC project (#173: silent exit for non-SDLC dirs)
10
16
  if find_sdlc_root; then
11
17
  PROJECT_DIR="$SDLC_ROOT"
@@ -2,6 +2,15 @@
2
2
  # PreToolUse hook - TDD enforcement before editing source files
3
3
  # Fires before Write/Edit/MultiEdit tools
4
4
 
5
+ # Token-bloat fix: when both project + plugin register this hook, plugin yields.
6
+ # Parameter-expansion-safe (no `dirname` dep): `%/*` strips trailing `/file`.
7
+ # Fallback `.` when BASH_SOURCE has no slash (direct invocation `bash hook.sh`).
8
+ HOOK_DIR="${BASH_SOURCE[0]%/*}"
9
+ [ "$HOOK_DIR" = "${BASH_SOURCE[0]}" ] && HOOK_DIR="."
10
+ # shellcheck disable=SC1091
11
+ source "$HOOK_DIR/_find-sdlc-root.sh"
12
+ dedupe_plugin_or_project "${BASH_SOURCE[0]}" || exit 0
13
+
5
14
  # Read the tool input (JSON with file_path, content, etc.)
6
15
  TOOL_INPUT=$(cat)
7
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-sdlc-wizard",
3
- "version": "1.37.0",
3
+ "version": "1.37.1",
4
4
  "description": "SDLC enforcement for Claude Code — hooks, skills, and wizard setup in one command",
5
5
  "bin": {
6
6
  "sdlc-wizard": "cli/bin/sdlc-wizard.js"
@@ -46,9 +46,10 @@ Parse all CHANGELOG entries between the user's installed version and the latest.
46
46
 
47
47
  ```
48
48
  Installed: 1.24.0
49
- Latest: 1.37.0
49
+ Latest: 1.37.1
50
50
 
51
51
  What changed:
52
+ - [1.37.1] Token-bloat fix: dedupe 2× SDLC BASELINE print when both project + plugin register the same hook (~300 tokens doubled per prompt). 5 hooks gain `dedupe_plugin_or_project()` helper. Codex 2-round 100/100.
52
53
  - [1.37.0] `monthly-research.yml` workflow deleted (ROADMAP #231 Phase 1) — 0 merged artifacts in 30d while burning $11-23/month; research happens inline now. `model-effort-check.sh` loud WARNING below xhigh (#217) — max preferred, xhigh floor; duplicate effort nudge in `instructions-loaded-check.sh` removed; single source of truth. Both changes Codex-certified.
53
54
  - [1.36.1] Repo renamed `agentic-ai-sdlc-wizard` → `claude-sdlc-wizard` (matches sibling pattern; npm package unchanged); `npm pkg fix` metadata cleanup; slug migration across docs/tests/configs
54
55
  - [1.36.0] CC 2.1.118 `/usage` canonical + aliases, Tier 2 dead-gate fix (#215), score-history max_score correctness (#211), setup-bun regression guard (#210), post-mortem learnings (#220-222), GPT-5.5 adoption plan (#223), MCP-tool hooks + #198 re-verify in backlog (#218/#219)