agentic-sdlc-wizard 1.45.0 → 1.46.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.
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@ 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.46.1] - 2026-04-27
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`npx check` surfaces dangling+enabled plugin state** (closes #266). Consumer disabled the wizard plugin via directory rename — CC's plugin loader still tried to resolve the missing path because `~/.claude/settings.json` `enabledPlugins["sdlc-wizard@sdlc-wizard-local"] = true` was untouched. Result: every UserPromptSubmit hook crashed silently for 3 days. The wizard can't fix CC's plugin loader, but `npx agentic-sdlc-wizard check` now cross-references `enabledPlugins` against DANGLING marketplace paths and surfaces a `CRASH RISK` block with the exact remediation: edit `~/.claude/settings.json` to flip the boolean to `false`, OR run `/plugin uninstall`. Strict-boolean check (only literal `true` triggers); scoped npm package keys parse correctly via `lastIndexOf('@')`. 3 new tests; Codex CERTIFIED 10/10 round 1.
|
|
12
|
+
|
|
13
|
+
### Files
|
|
14
|
+
|
|
15
|
+
- `cli/init.js` — `checkMarketplacePaths()` now reads `enabledPlugins`; result objects gain `crashRisk: bool` + `enabledPluginKey: string|null`; print loop surfaces actionable `CRASH RISK` block when both DANGLING + enabled hold
|
|
16
|
+
- `tests/test-cli.sh` — 3 new tests (positive crash-risk fires, negative without enabled, negative with disabled)
|
|
17
|
+
|
|
18
|
+
## [1.46.0] - 2026-04-27
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **PreCompact dry-run env vars** (closes #240). Consumer reported clobbering their real `.reviews/handoff.json` while smoke-testing the PreCompact hook — the only way to verify hook behavior was to `cp` real state aside, fabricate fakes, and restore. Two new env vars simulate state in-memory:
|
|
23
|
+
- `SDLC_DRY_RUN_HANDOFF_STATUS=<value>` — overrides the handoff.json read entirely. Useful values: `PENDING_REVIEW`/`PENDING_RECHECK` (block), `CERTIFIED` (silent). Skips file I/O.
|
|
24
|
+
- `SDLC_DRY_RUN_GIT_STATE=rebase|merge|cherry-pick` — simulates an in-flight git op. No real `.git/` needed.
|
|
25
|
+
- **Safety**: unknown values (typos like `bogus`) fall back to real-state checks rather than silently bypassing safety. Codex round 1 caught this bypass risk; the fix uses a `DRY_RUN_GIT_HANDLED` flag so only known scenarios short-circuit the real check.
|
|
26
|
+
- **No mutations**: dry-run paths are pure read-only simulation. Subsequent runs without env vars see clean state.
|
|
27
|
+
- 7 new test-hooks tests (positive simulations + override of real PENDING + typo fallback + no-mutation guarantee). Codex round 2 CERTIFIED 10/10.
|
|
28
|
+
|
|
29
|
+
### Files
|
|
30
|
+
|
|
31
|
+
- `hooks/precompact-seam-check.sh` — comment block + dry-run handoff `if/elif` branch + dry-run git `case` with `DRY_RUN_GIT_HANDLED` flag; real-state check gated on flag
|
|
32
|
+
- `tests/test-hooks.sh` — 7 new dry-run tests
|
|
33
|
+
|
|
7
34
|
## [1.45.0] - 2026-04-27
|
|
8
35
|
|
|
9
36
|
### Added
|
|
@@ -2967,7 +2967,7 @@ If deployment fails or post-deploy verification catches issues:
|
|
|
2967
2967
|
|
|
2968
2968
|
**SDLC.md:**
|
|
2969
2969
|
```markdown
|
|
2970
|
-
<!-- SDLC Wizard Version: 1.
|
|
2970
|
+
<!-- SDLC Wizard Version: 1.46.1 -->
|
|
2971
2971
|
<!-- Setup Date: [DATE] -->
|
|
2972
2972
|
<!-- 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 -->
|
|
2973
2973
|
<!-- Git Workflow: [PRs or Solo] -->
|
|
@@ -4032,7 +4032,7 @@ Walk through updates? (y/n)
|
|
|
4032
4032
|
Store wizard state in `SDLC.md` as metadata comments (invisible to readers, parseable by Claude):
|
|
4033
4033
|
|
|
4034
4034
|
```markdown
|
|
4035
|
-
<!-- SDLC Wizard Version: 1.
|
|
4035
|
+
<!-- SDLC Wizard Version: 1.46.1 -->
|
|
4036
4036
|
<!-- Setup Date: 2026-01-24 -->
|
|
4037
4037
|
<!-- 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 -->
|
|
4038
4038
|
<!-- Git Workflow: PRs -->
|
package/cli/init.js
CHANGED
|
@@ -385,6 +385,12 @@ function check(targetDir, { json = false } = {}) {
|
|
|
385
385
|
console.log(`\n ${color}${m.status}${RESET} ${heading}`);
|
|
386
386
|
console.log(` ${m.path}`);
|
|
387
387
|
console.log(` ${m.details}`);
|
|
388
|
+
// #266: DANGLING + enabledPlugins=true = every UserPromptSubmit hook
|
|
389
|
+
// crashes. Surface the actionable fix loud and clear.
|
|
390
|
+
if (m.crashRisk && m.enabledPluginKey) {
|
|
391
|
+
console.log(` ${RED}CRASH RISK${RESET}: enabledPlugins["${m.enabledPluginKey}"] is true but the path is missing — every UserPromptSubmit hook will fail until this is resolved (#266)`);
|
|
392
|
+
console.log(` Fix: edit ~/.claude/settings.json and set enabledPlugins["${m.enabledPluginKey}"] to false, OR run /plugin uninstall to clean up properly`);
|
|
393
|
+
}
|
|
388
394
|
if (m.suggestion) {
|
|
389
395
|
console.log(` Recommended: move to ${m.suggestion}`);
|
|
390
396
|
}
|
|
@@ -453,6 +459,28 @@ function checkMarketplacePaths() {
|
|
|
453
459
|
const marketplaces = data.extraKnownMarketplaces;
|
|
454
460
|
if (!marketplaces || typeof marketplaces !== 'object') return results;
|
|
455
461
|
|
|
462
|
+
// #266: cross-reference enabledPlugins to detect the actual crash state.
|
|
463
|
+
// A DANGLING marketplace path is harmless if no plugins from it are enabled,
|
|
464
|
+
// but DANGLING + enabled = every UserPromptSubmit hook crashes (CC's plugin
|
|
465
|
+
// loader fails to resolve the path and propagates the error).
|
|
466
|
+
// Plugin keys in enabledPlugins are formatted "<plugin>@<marketplace>".
|
|
467
|
+
const enabledPlugins = (data.enabledPlugins && typeof data.enabledPlugins === 'object')
|
|
468
|
+
? data.enabledPlugins
|
|
469
|
+
: {};
|
|
470
|
+
function findEnabledPluginForMarketplace(marketplaceName) {
|
|
471
|
+
for (const [pluginKey, isEnabled] of Object.entries(enabledPlugins)) {
|
|
472
|
+
if (isEnabled !== true) continue;
|
|
473
|
+
// pluginKey shape: "sdlc-wizard@sdlc-wizard-local". The part after the
|
|
474
|
+
// last `@` is the marketplace name. Use lastIndexOf so plugin names
|
|
475
|
+
// containing `@` (npm scoped packages) parse correctly.
|
|
476
|
+
const atIdx = pluginKey.lastIndexOf('@');
|
|
477
|
+
if (atIdx <= 0) continue;
|
|
478
|
+
const mp = pluginKey.slice(atIdx + 1);
|
|
479
|
+
if (mp === marketplaceName) return pluginKey;
|
|
480
|
+
}
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
|
|
456
484
|
for (const [name, entry] of Object.entries(marketplaces)) {
|
|
457
485
|
const source = entry && entry.source;
|
|
458
486
|
if (!source || source.source !== 'directory' || !source.path || typeof source.path !== 'string') continue;
|
|
@@ -464,10 +492,14 @@ function checkMarketplacePaths() {
|
|
|
464
492
|
const suggestion = `~/.claude/plugins-local/${basename}`;
|
|
465
493
|
|
|
466
494
|
if (!exists) {
|
|
495
|
+
const enabledPluginKey = findEnabledPluginForMarketplace(name);
|
|
496
|
+
const isCrashRisk = enabledPluginKey !== null;
|
|
467
497
|
results.push({
|
|
468
498
|
name,
|
|
469
499
|
path: sourcePath,
|
|
470
500
|
status: 'DANGLING',
|
|
501
|
+
crashRisk: isCrashRisk,
|
|
502
|
+
enabledPluginKey,
|
|
471
503
|
details: isEphemeral
|
|
472
504
|
? 'Ephemeral path has been reaped — plugin is broken'
|
|
473
505
|
: 'Path does not exist — plugin may be silently broken',
|
|
@@ -30,6 +30,16 @@ ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
|
30
30
|
|
|
31
31
|
HOLD_REASONS=""
|
|
32
32
|
|
|
33
|
+
# #240: Dry-run / simulation env vars. Let consumers verify hook behavior
|
|
34
|
+
# without mutating real .reviews/handoff.json or .git/ state.
|
|
35
|
+
# SDLC_DRY_RUN_HANDOFF_STATUS=<value> — overrides handoff.json status
|
|
36
|
+
# lookup (skip the file read entirely)
|
|
37
|
+
# SDLC_DRY_RUN_GIT_STATE=rebase|merge|cherry-pick — simulates an in-flight
|
|
38
|
+
# git operation
|
|
39
|
+
# When set, dry-run values short-circuit the real-state checks below. The
|
|
40
|
+
# hook still emits the same HOLD/silent decision so consumers can smoke-test
|
|
41
|
+
# every code path. No filesystem writes — purely read-only simulation.
|
|
42
|
+
|
|
33
43
|
# Check 1: Codex review mid-cycle
|
|
34
44
|
# Self-heal paths (ordered by preference):
|
|
35
45
|
# (a) #209: handoff has pr_number + gh reports PR MERGED → implicit CERTIFIED (silent)
|
|
@@ -54,7 +64,16 @@ case "$STALE_DAYS_RAW" in
|
|
|
54
64
|
*) STALE_DAYS="$STALE_DAYS_RAW" ;;
|
|
55
65
|
esac
|
|
56
66
|
STALE_WARN=""
|
|
57
|
-
|
|
67
|
+
# #240: dry-run override skips the real handoff.json read.
|
|
68
|
+
if [ -n "${SDLC_DRY_RUN_HANDOFF_STATUS:-}" ]; then
|
|
69
|
+
STATUS="$SDLC_DRY_RUN_HANDOFF_STATUS"
|
|
70
|
+
case "$STATUS" in
|
|
71
|
+
PENDING_REVIEW|PENDING_RECHECK)
|
|
72
|
+
HOLD_REASONS="${HOLD_REASONS} - Codex review is ${STATUS}. Round-1 evidence lives in this context — compacting now loses what round-2 needs to re-verify.
|
|
73
|
+
Resolve: wait for CERTIFIED (or escalate) before /compact."$'\n'
|
|
74
|
+
;;
|
|
75
|
+
esac
|
|
76
|
+
elif [ -f "$HANDOFF" ]; then
|
|
58
77
|
STATUS=$(grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' "$HANDOFF" 2>/dev/null | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
59
78
|
case "$STATUS" in
|
|
60
79
|
PENDING_REVIEW|PENDING_RECHECK)
|
|
@@ -168,8 +187,37 @@ if [ -f "$HANDOFF" ]; then
|
|
|
168
187
|
fi
|
|
169
188
|
|
|
170
189
|
# Check 2: in-progress git operation
|
|
190
|
+
# #240: dry-run override simulates a git op without needing a real .git/.
|
|
171
191
|
GITDIR="$ROOT/.git"
|
|
172
|
-
|
|
192
|
+
# Step 1: when dry-run var matches a known scenario, simulate it.
|
|
193
|
+
# Otherwise (unset, empty, or unknown value) fall through to the real
|
|
194
|
+
# .git/ checks below — this prevents an unintended safety bypass when
|
|
195
|
+
# the user typos the env var (e.g. SDLC_DRY_RUN_GIT_STATE=bogus would
|
|
196
|
+
# previously skip real checks entirely; Codex P1 round 1).
|
|
197
|
+
DRY_RUN_GIT_HANDLED=0
|
|
198
|
+
case "${SDLC_DRY_RUN_GIT_STATE:-}" in
|
|
199
|
+
rebase)
|
|
200
|
+
HOLD_REASONS="${HOLD_REASONS} - Git rebase in progress. Compacting mid-rebase loses the operation's context.
|
|
201
|
+
Resolve: finish or abort the rebase before /compact."$'\n'
|
|
202
|
+
DRY_RUN_GIT_HANDLED=1
|
|
203
|
+
;;
|
|
204
|
+
merge)
|
|
205
|
+
HOLD_REASONS="${HOLD_REASONS} - Git merge in progress. Compacting mid-merge loses the operation's context.
|
|
206
|
+
Resolve: finish or abort the merge before /compact."$'\n'
|
|
207
|
+
DRY_RUN_GIT_HANDLED=1
|
|
208
|
+
;;
|
|
209
|
+
cherry-pick)
|
|
210
|
+
HOLD_REASONS="${HOLD_REASONS} - Git cherry-pick in progress. Compacting mid-cherry-pick loses the operation's context.
|
|
211
|
+
Resolve: finish or abort the cherry-pick before /compact."$'\n'
|
|
212
|
+
DRY_RUN_GIT_HANDLED=1
|
|
213
|
+
;;
|
|
214
|
+
esac
|
|
215
|
+
|
|
216
|
+
# Step 2: real .git/ check fires if dry-run didn't simulate a scenario.
|
|
217
|
+
# Empty/unset SDLC_DRY_RUN_GIT_STATE → real check (default behavior).
|
|
218
|
+
# Unknown value (e.g. typo "bogus") → also falls through to real check
|
|
219
|
+
# rather than silently bypassing safety. The safer-than-the-typo path.
|
|
220
|
+
if [ "$DRY_RUN_GIT_HANDLED" -eq 0 ] && [ -d "$GITDIR" ]; then
|
|
173
221
|
if [ -e "$GITDIR/REBASE_HEAD" ] || [ -d "$GITDIR/rebase-merge" ] || [ -d "$GITDIR/rebase-apply" ]; then
|
|
174
222
|
HOLD_REASONS="${HOLD_REASONS} - Git rebase in progress. Compacting mid-rebase loses the operation's context.
|
|
175
223
|
Resolve: finish or abort the rebase before /compact."$'\n'
|
package/package.json
CHANGED
package/skills/update/SKILL.md
CHANGED
|
@@ -131,9 +131,11 @@ Parse all CHANGELOG entries between the user's installed version and the latest.
|
|
|
131
131
|
|
|
132
132
|
```
|
|
133
133
|
Installed: 1.24.0
|
|
134
|
-
Latest: 1.
|
|
134
|
+
Latest: 1.46.1
|
|
135
135
|
|
|
136
136
|
What changed:
|
|
137
|
+
- [1.46.1] `npx check` surfaces dangling+enabled plugin state — closes #266. Consumer disabled the wizard plugin via directory rename without flipping `enabledPlugins[sdlc-wizard@sdlc-wizard-local]` in `~/.claude/settings.json`. CC's plugin loader tried to resolve the missing path and crashed UserPromptSubmit on every prompt for **3 days** before the consumer noticed. The wizard's `check` subcommand now cross-references `enabledPlugins` against DANGLING marketplace paths: when both hold (path missing AND plugin enabled), prints a loud `CRASH RISK` block with the exact remediation (flip the boolean to false OR run `/plugin uninstall`). 3 new tests, Codex round 1 CERTIFIED 10/10.
|
|
138
|
+
- [1.46.0] PreCompact dry-run env vars — closes #240. Smoke-testing PreCompact previously required cp'ing real `.reviews/handoff.json` and `.git/` aside, fabricating fake state, restoring — error-prone (consumer clobbered real handoff.json mid-test). Two new env vars: `SDLC_DRY_RUN_HANDOFF_STATUS=PENDING_RECHECK|CERTIFIED|...` simulates handoff status (overrides the real file read); `SDLC_DRY_RUN_GIT_STATE=rebase|merge|cherry-pick` simulates an in-flight git op (no real .git/ needed). Empty/unset → real-state checks (no behavior change). Unknown values (typos) → fall back to real check, NOT silent bypass — Codex round 1 caught the bypass risk and we fixed it with a DRY_RUN_GIT_HANDLED flag. 7 new test-hooks tests. Codex round 2 CERTIFIED 10/10.
|
|
137
139
|
- [1.45.0] PreCompact path (c) — SHA-ancestry self-heal — closes #257. Solo-developer pattern: write fixes, commit them, run targeted Codex recheck, see CERTIFIED in `.reviews/latest-review.md`, ship the feature. Forgetting to bump `handoff.json status` from `PENDING_RECHECK` → `CERTIFIED` is realistic — the file is buried and the visible signals (commits + review file) already say "done". PreCompact hook now self-heals silently when: (a) handoff is `PENDING_*` with no `pr_number`, (b) every SHA cited in `fixes_applied[]` is reachable from HEAD via `git merge-base --is-ancestor`, AND (c) `.reviews/latest-review.md` contains CERTIFIED without `NOT CERTIFIED`. Bracket extraction is escape-aware (depth counter + JSON `\\\"` handling) so `]` inside string literals doesn't terminate the array early. UUIDs (8-4-4-4-12 hex) stripped before SHA extraction so ticket IDs in fixes_applied don't false-block the heal. 9 new tests, Codex round 3 CERTIFIED 10/10.
|
|
138
140
|
- [1.44.1] Autocompact compound-misconfig detection — closes #207. Consumer reported autocompact firing at 12% context on a fresh opus[1m] session because they set BOTH `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=30` AND `CLAUDE_CODE_AUTO_COMPACT_WINDOW=400000` (a natural misreading of the "or"-joined override cell). The two compound: 30% × 400K = 120K trigger ≈ 12% of 1M. Three-pronged fix: (a) wizard doc clarifies alternatives with a `> ⚠ Do NOT set both` callout that shows the compound math; (b) `instructions-loaded-check.sh` (InstructionsLoaded hook) detects when both env vars are set in `.claude/settings.json`, computes the effective trigger, and warns with the math; (c) shipped `skills/sdlc/SKILL.md` was still calling opus[1m] the "default" (stale post-#198) AND repeating the same ambiguous wording — both fixed. 4 new hook tests + 3 new doc-consistency tests + size-cap fixture extended. Codex round 2 CERTIFIED 9/10.
|
|
139
141
|
- [1.44.0] Install-path & cache hygiene — closes #254, #239, #238 filed by consumer codeguesser after upgrading 1.32.0 → 1.42.1. (1) `cli/init.js` FILES list now ships `hooks/_find-sdlc-root.sh` — the helper sourced by all 5 hooks was missing from npm install path, so every session emitted `_find-sdlc-root.sh: No such file or directory` + `dedupe_plugin_or_project: command not found` and the SDLC walk-up logic was silently dead. (2) `init --force` now invalidates `~/.cache/sdlc-wizard/latest-version` so post-upgrade hooks re-fetch fresh values from npm instead of serving the pre-upgrade cache for 24h (which produced reverse "1.42.1 → 1.41.1" nudges). (3) instructions-loaded-check.sh now uses semver-direction comparison via new `semver_lt` function: nudge only fires when installed < latest, equality is silent, reverse direction is silent. Cache sanity-check rejects poisoned values (cached "latest" < installed → force refetch). (4) When `npm view` fails AND cache empty, hook now surfaces a one-line warning instead of going silent. (5) Dual-channel install nudge gains an opt-in silence sentinel — set via `mkdir -p $SDLC_WIZARD_CACHE_DIR && touch $SDLC_WIZARD_CACHE_DIR/dual-channel-acknowledged` (printed inside the nudge itself for discoverability). 8 new tests across test-cli.sh + test-hooks.sh, Codex CERTIFIED 10/10 round 2.
|