@qball-inc/the-bulwark 1.0.1 → 1.1.0
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/.claude-plugin/plugin.json +2 -3
- package/LICENSE +21 -0
- package/README.md +41 -1
- package/hooks/hooks.json +11 -0
- package/package.json +1 -1
- package/scripts/hooks/enforce-quality.sh +70 -21
- package/scripts/hooks/suggest-pipeline-stop.sh +118 -0
- package/scripts/hooks/suggest-pipeline.sh +12 -0
- package/scripts/init.sh +8 -0
- package/scripts/install-just.sh +404 -0
- package/scripts/toolchain-smoke-run.sh +219 -0
- package/skills/bulwark-scaffold/SKILL.md +99 -33
- package/skills/init/SKILL.md +102 -6
- package/skills/session-handoff/SKILL.md +26 -0
- package/skills/test-audit/scripts/data-flow-analyzer.ts +1 -2
- package/skills/test-audit/scripts/skip-detector.ts +1 -1
- package/skills/test-audit/scripts/verification-counter.ts +1 -1
- package/Infographics/01_product-ideation.png +0 -0
- package/Infographics/02_feature-research.png +0 -0
- package/Infographics/03_brainstorm.png +0 -0
- package/Infographics/04_plan-creation.png +0 -0
- package/Infographics/05_code-review.png +0 -0
- package/Infographics/06_test-audit.png +0 -0
- package/Infographics/07_fix-bug.png +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "the-bulwark",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Full-lifecycle SDLC guardrailing framework for Claude Code — from product ideation and planning through implementation, code review, and test validation. Enterprise-grade skills and agents for AI-human peer collaboration.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ashay Kubal",
|
|
@@ -38,6 +38,5 @@
|
|
|
38
38
|
"test-coverage",
|
|
39
39
|
"statusline",
|
|
40
40
|
"agent-teams"
|
|
41
|
-
]
|
|
42
|
-
"hooks": "./hooks/hooks.json"
|
|
41
|
+
]
|
|
43
42
|
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ashay Kubal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
<a href="#how-it-works">How it works</a> ·
|
|
16
16
|
<a href="#hooks">Hooks</a> ·
|
|
17
17
|
<a href="#skill-registry">Skills</a> ·
|
|
18
|
-
<a href="#agent-registry">Agents</a>
|
|
18
|
+
<a href="#agent-registry">Agents</a> ·
|
|
19
|
+
<a href="#planned-enhancements">Roadmap</a>
|
|
19
20
|
</p>
|
|
20
21
|
|
|
21
22
|
<p align="center">
|
|
@@ -92,6 +93,8 @@ After installing, restart your Claude Code session and run the init skill:
|
|
|
92
93
|
|
|
93
94
|
This walks you through a guided setup: Rules.md injection, CLAUDE.md configuration, and optional tooling (LSP, Justfile scaffolding, statusline). It auto-detects brownfield projects and adjusts accordingly.
|
|
94
95
|
|
|
96
|
+
> Having trouble installing? See [FAQ and troubleshooting](#faq-and-troubleshooting). If your issue isn't covered, please [open an issue](https://github.com/QBall-Inc/the-bulwark/issues).
|
|
97
|
+
|
|
95
98
|
## Prerequisites
|
|
96
99
|
|
|
97
100
|
| Requirement | Details |
|
|
@@ -297,6 +300,23 @@ Agents are single-purpose sub-agents spawned by skills via the Task tool. You do
|
|
|
297
300
|
|
|
298
301
|
## FAQ and troubleshooting
|
|
299
302
|
|
|
303
|
+
### Plugin clone fails with "Permission denied (publickey)"
|
|
304
|
+
|
|
305
|
+
If you see this error when installing from the marketplace:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
git@github.com: Permission denied (publickey).
|
|
309
|
+
fatal: Could not read from remote repository.
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Your git is defaulting to SSH for GitHub, but you don't have SSH keys configured. Fix by telling git to use HTTPS:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
git config --global url."https://github.com/".insteadOf "git@github.com:"
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Then retry the install. This applies globally and redirects all GitHub SSH URLs to HTTPS.
|
|
319
|
+
|
|
300
320
|
### Hooks aren't firing after install
|
|
301
321
|
|
|
302
322
|
Restart your Claude Code session. Hooks only load at session start. If they still don't fire, check that the plugin is installed:
|
|
@@ -357,6 +377,26 @@ You can't disable individual plugin hooks without modifying `hooks/hooks.json` i
|
|
|
357
377
|
|
|
358
378
|
---
|
|
359
379
|
|
|
380
|
+
## Planned enhancements
|
|
381
|
+
|
|
382
|
+
These are on the roadmap. No timeline commitments, but they represent the direction The Bulwark is heading.
|
|
383
|
+
|
|
384
|
+
**Evaluation framework.** Skills and agents are the new code layer in agentic development. They need the same rigor as code: versioned, tested, measured. We're building two new skills — `create-eval` and `run-eval` — that generate and execute evaluations for any Claude Code asset. Define test prompts, expected outputs, and grading criteria. Run them across skill versions to catch regressions. Measure conversational invocation success, checklist compliance, and output quality with structured grading reports.
|
|
385
|
+
|
|
386
|
+
**Asset baselines.** Once the eval skills exist, we'll baseline all 28 skills and 15 agents with versioned evaluations. Every asset gets a `version` field in its frontmatter and a set of evals that serve as regression references. Future changes get measured against these baselines automatically.
|
|
387
|
+
|
|
388
|
+
**Enterprise traceability.** Enhanced logging with version stamps (skill version, model, rules hash) in every sub-agent log header. Run manifests that tie together all artifacts from a pipeline execution into a single auditable record. Decision lineage: trace any output back to which model, skill version, and rules produced it.
|
|
389
|
+
|
|
390
|
+
**Security pattern updates.** A helper skill that pulls the latest vulnerability patterns and edge cases into the test-audit pipeline. Keeps your security coverage current without manual curation.
|
|
391
|
+
|
|
392
|
+
**Framework-specific Justfiles.** Auto-detect your project's framework (Next.js, Django, FastAPI, Actix, etc.) and generate tailored `just` recipes with the right build, test, and lint commands out of the box.
|
|
393
|
+
|
|
394
|
+
**Agent memory.** Persistent memory for sub-agents across invocations. Agents remember patterns from previous runs — common failure modes, project-specific conventions, recurring issues — and apply that context automatically.
|
|
395
|
+
|
|
396
|
+
**Smarter pipeline routing.** Better orchestration for review-then-fix workflows. When a code review finds issues, automatically route to fix validation without manual intervention. Tighter feedback loops between review, fix, and retest stages.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
360
400
|
## License
|
|
361
401
|
|
|
362
402
|
[MIT](LICENSE)
|
package/hooks/hooks.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qball-inc/the-bulwark",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Full-lifecycle SDLC guardrailing framework for Claude Code — from product ideation and planning through implementation, code review, and test validation. Enterprise-grade skills and agents for AI-human peer collaboration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ashay Kubal <https://ashaykubal.com>",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# enforce-quality.sh - Quality gate +
|
|
2
|
+
# enforce-quality.sh - Quality gate + changed-files accumulator
|
|
3
3
|
#
|
|
4
4
|
# Called by PostToolUse hooks on Write|Edit operations.
|
|
5
5
|
#
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
# - Runs just typecheck, lint, build
|
|
8
8
|
# - Exit 2 on failure (blocks tool call)
|
|
9
9
|
#
|
|
10
|
-
# Phase 2:
|
|
11
|
-
# -
|
|
12
|
-
# -
|
|
10
|
+
# Phase 2: Accumulator write (code + script files only)
|
|
11
|
+
# - Appends file path to tmp/bulwark-changed-files.json
|
|
12
|
+
# - Excludes docs, config, infra paths via extension + prefix filter
|
|
13
|
+
# - Dedup on path
|
|
14
|
+
# - Stop hook (suggest-pipeline-stop.sh) reads accumulator and emits
|
|
15
|
+
# exactly ONE pipeline-suggestion block per turn — replacing the legacy
|
|
16
|
+
# per-edit decision:block anti-pattern (hook storm / silent crash).
|
|
13
17
|
#
|
|
14
18
|
# Exit codes:
|
|
15
|
-
# 0 = All checks passed,
|
|
19
|
+
# 0 = All checks passed, accumulator updated
|
|
16
20
|
# 2 = Quality gate failed (block)
|
|
17
21
|
#
|
|
18
22
|
# Usage: Called by hooks, not directly by users
|
|
@@ -22,14 +26,11 @@ set -euo pipefail
|
|
|
22
26
|
# Configuration
|
|
23
27
|
MAX_OUTPUT_LINES=100
|
|
24
28
|
|
|
25
|
-
# Color codes (if terminal supports)
|
|
26
|
-
RED='\033[0;31m'
|
|
27
|
-
GREEN='\033[0;32m'
|
|
29
|
+
# Color codes (if terminal supports) — used for warning banners only
|
|
28
30
|
YELLOW='\033[0;33m'
|
|
29
31
|
NC='\033[0m' # No Color
|
|
30
32
|
|
|
31
33
|
# Get directories
|
|
32
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
33
34
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
34
35
|
LOGS_DIR="${PROJECT_DIR}/logs"
|
|
35
36
|
HOOKS_LOG="${LOGS_DIR}/hooks.log"
|
|
@@ -48,10 +49,18 @@ echo "[${TIMESTAMP}] PostToolUse: enforce-quality.sh triggered for ${FILE_PATH_F
|
|
|
48
49
|
# Extract file path from input
|
|
49
50
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
50
51
|
|
|
51
|
-
#
|
|
52
|
+
# Normalize absolute path to repo-relative (if under PROJECT_DIR)
|
|
53
|
+
REL_PATH="$FILE_PATH"
|
|
54
|
+
if [ -n "$FILE_PATH" ] && [ "${PROJECT_DIR}" != "." ] && [[ "$REL_PATH" == "$PROJECT_DIR/"* ]]; then
|
|
55
|
+
REL_PATH="${REL_PATH#"$PROJECT_DIR/"}"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Skip infrastructure directories (no quality checks or pipeline suggestions).
|
|
59
|
+
# Matches against REPO-RELATIVE path — absolute-path glob matching was a bug:
|
|
60
|
+
# any project under a path containing /tmp/, /logs/, etc. was ignored entirely.
|
|
52
61
|
# DEF-005: Prevents infinite loops when writing to logs/
|
|
53
|
-
case "$
|
|
54
|
-
|
|
62
|
+
case "$REL_PATH" in
|
|
63
|
+
logs/*|tmp/*|.claude/*|node_modules/*|dist/*|build/*|.git/*)
|
|
55
64
|
exit 0
|
|
56
65
|
;;
|
|
57
66
|
esac
|
|
@@ -145,22 +154,62 @@ if is_code_file "$FILE_PATH"; then
|
|
|
145
154
|
}
|
|
146
155
|
fi
|
|
147
156
|
|
|
148
|
-
# Export quality results for suggest-pipeline.sh
|
|
149
|
-
export QUALITY_CHECKS_PASSED="true"
|
|
150
157
|
fi
|
|
151
158
|
fi
|
|
152
159
|
fi
|
|
153
160
|
|
|
154
161
|
# ============================================================
|
|
155
|
-
# PHASE 2:
|
|
162
|
+
# PHASE 2: Accumulator Write (code + script files only)
|
|
156
163
|
# ============================================================
|
|
164
|
+
#
|
|
165
|
+
# Legacy Phase 2 chained to suggest-pipeline.sh, which emitted one
|
|
166
|
+
# decision:block per edit — cascading into N interrupts on multi-edit turns.
|
|
167
|
+
# Replaced by: append qualifying file path to accumulator; Stop hook
|
|
168
|
+
# (suggest-pipeline-stop.sh) emits exactly one block at turn end.
|
|
169
|
+
|
|
170
|
+
# Skip accumulator write if no file path (defensive)
|
|
171
|
+
if [ -z "$FILE_PATH" ]; then
|
|
172
|
+
exit 0
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# --- Exclusion filter: extensions ---
|
|
176
|
+
# Docs, config, data files do not warrant pipeline orchestration.
|
|
177
|
+
# Prefix exclusions (logs/, tmp/, .claude/, node_modules/, dist/, build/, .git/)
|
|
178
|
+
# already exited at the top-of-file skip. REL_PATH is also pre-computed there.
|
|
179
|
+
FILENAME=$(basename "$FILE_PATH")
|
|
180
|
+
EXTENSION="${FILENAME##*.}"
|
|
181
|
+
EXTENSION_LOWER=$(echo "$EXTENSION" | tr '[:upper:]' '[:lower:]')
|
|
182
|
+
|
|
183
|
+
case "$EXTENSION_LOWER" in
|
|
184
|
+
md|markdown|json|jsonc|yaml|yml|xml|csv|tsv|txt|docx|xlsx|pdf|html|htm)
|
|
185
|
+
exit 0
|
|
186
|
+
;;
|
|
187
|
+
esac
|
|
188
|
+
|
|
189
|
+
# --- Accumulator: ensure directory, validate, append with dedup ---
|
|
190
|
+
ACCUMULATOR_DIR="${PROJECT_DIR}/tmp"
|
|
191
|
+
ACCUMULATOR="${ACCUMULATOR_DIR}/bulwark-changed-files.json"
|
|
192
|
+
mkdir -p "$ACCUMULATOR_DIR"
|
|
157
193
|
|
|
158
|
-
|
|
194
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
|
|
195
|
+
ACC_TIMESTAMP=$(date -Iseconds)
|
|
196
|
+
|
|
197
|
+
# Recover from corrupt accumulator
|
|
198
|
+
if [ -f "$ACCUMULATOR" ] && ! jq -e '.' "$ACCUMULATOR" >/dev/null 2>&1; then
|
|
199
|
+
rm -f "$ACCUMULATOR"
|
|
200
|
+
fi
|
|
159
201
|
|
|
160
|
-
if [ -
|
|
161
|
-
|
|
162
|
-
|
|
202
|
+
if [ ! -f "$ACCUMULATOR" ]; then
|
|
203
|
+
jq -n --arg p "$REL_PATH" --arg t "$TOOL_NAME" --arg ts "$ACC_TIMESTAMP" \
|
|
204
|
+
'{version: "1.0", files: [{path: $p, tool: $t, time: $ts}]}' > "$ACCUMULATOR"
|
|
163
205
|
else
|
|
164
|
-
#
|
|
165
|
-
|
|
206
|
+
# Dedup: append only if path not already present
|
|
207
|
+
EXISTING=$(jq -r --arg p "$REL_PATH" '.files[] | select(.path == $p) | .path' "$ACCUMULATOR" 2>/dev/null || echo "")
|
|
208
|
+
if [ -z "$EXISTING" ]; then
|
|
209
|
+
jq --arg p "$REL_PATH" --arg t "$TOOL_NAME" --arg ts "$ACC_TIMESTAMP" \
|
|
210
|
+
'.files += [{path: $p, tool: $t, time: $ts}]' "$ACCUMULATOR" > "${ACCUMULATOR}.tmp" \
|
|
211
|
+
&& mv "${ACCUMULATOR}.tmp" "$ACCUMULATOR"
|
|
212
|
+
fi
|
|
166
213
|
fi
|
|
214
|
+
|
|
215
|
+
exit 0
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# suggest-pipeline-stop.sh - Stop-hook handler for consolidated pipeline suggestions
|
|
3
|
+
#
|
|
4
|
+
# Fires at the end of every Claude turn. Reads the changed-files accumulator
|
|
5
|
+
# written by enforce-quality.sh and emits exactly one `decision: block`
|
|
6
|
+
# pipeline suggestion per turn, citing all qualifying files.
|
|
7
|
+
#
|
|
8
|
+
# Replaces the PostToolUse-per-edit `decision: block` anti-pattern from
|
|
9
|
+
# legacy suggest-pipeline.sh, which cascaded N interruptions on multi-edit
|
|
10
|
+
# turns (the "hook storm" / "silent crash").
|
|
11
|
+
#
|
|
12
|
+
# Input (stdin): Stop hook JSON per Anthropic spec:
|
|
13
|
+
# - stop_hook_active (bool) recursion guard; true if already firing
|
|
14
|
+
# - stop_reason (string) end_turn | user_interrupt | max_tokens
|
|
15
|
+
# - session_id (string) current session id
|
|
16
|
+
# - transcript_path (string) path to turn transcript
|
|
17
|
+
# - cwd (string) working directory
|
|
18
|
+
# - permission_mode (string) current permission mode
|
|
19
|
+
# - hook_event_name (string) always "Stop" here
|
|
20
|
+
#
|
|
21
|
+
# Output (stdout):
|
|
22
|
+
# - {"decision": "block", "reason": ...} when accumulator is non-empty;
|
|
23
|
+
# prevents Claude from ending the turn until the suggested pipeline runs.
|
|
24
|
+
# - {} otherwise (silent pass).
|
|
25
|
+
#
|
|
26
|
+
# NOTE: Stop hooks do NOT support `hookSpecificOutput` / `additionalContext`
|
|
27
|
+
# per the Claude Code schema (https://code.claude.com/docs/en/hooks). Valid
|
|
28
|
+
# top-level fields for Stop hooks: decision, reason, continue, stopReason,
|
|
29
|
+
# suppressOutput, systemMessage. All instruction content must live in `reason`.
|
|
30
|
+
#
|
|
31
|
+
# Accumulator contract: see scripts/hooks/enforce-quality.sh (writer).
|
|
32
|
+
# Path: ${CLAUDE_PROJECT_DIR:-$(pwd)}/tmp/bulwark-changed-files.json
|
|
33
|
+
# Schema: {"version": "1.0", "files": [{"path", "tool", "time"}]}
|
|
34
|
+
|
|
35
|
+
set -euo pipefail
|
|
36
|
+
|
|
37
|
+
# Read stdin once
|
|
38
|
+
INPUT=$(cat)
|
|
39
|
+
|
|
40
|
+
# Resolve project directory
|
|
41
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
42
|
+
LOGS_DIR="${PROJECT_DIR}/logs"
|
|
43
|
+
ACCUMULATOR="${PROJECT_DIR}/tmp/bulwark-changed-files.json"
|
|
44
|
+
|
|
45
|
+
mkdir -p "$LOGS_DIR"
|
|
46
|
+
HOOKS_LOG="${LOGS_DIR}/hooks.log"
|
|
47
|
+
TIMESTAMP=$(date -Iseconds)
|
|
48
|
+
|
|
49
|
+
# --- Recursion guard ---
|
|
50
|
+
# If a prior Stop hook already blocked and Claude is now responding to that
|
|
51
|
+
# block, stop_hook_active is true. Short-circuit to prevent infinite loops.
|
|
52
|
+
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
|
|
53
|
+
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
|
|
54
|
+
echo "[${TIMESTAMP}] Stop: recursion guard active, exiting silently" >> "$HOOKS_LOG"
|
|
55
|
+
echo '{}'
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# --- Accumulator absent or empty → silent pass ---
|
|
60
|
+
if [ ! -f "$ACCUMULATOR" ]; then
|
|
61
|
+
echo '{}'
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Validate JSON; corrupt → drop and exit silently
|
|
66
|
+
if ! jq -e '.' "$ACCUMULATOR" >/dev/null 2>&1; then
|
|
67
|
+
echo "[${TIMESTAMP}] Stop: corrupt accumulator, resetting" >> "$HOOKS_LOG"
|
|
68
|
+
rm -f "$ACCUMULATOR"
|
|
69
|
+
echo '{}'
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
FILE_COUNT=$(jq -r '.files | length' "$ACCUMULATOR" 2>/dev/null || echo "0")
|
|
74
|
+
if [ "$FILE_COUNT" = "0" ]; then
|
|
75
|
+
echo '{}'
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# --- Categorize files for targeted pipeline suggestion ---
|
|
80
|
+
CODE_FILES=$(jq -r '[.files[] | select(.path | test("\\.(ts|tsx|js|jsx|py|go|rs|java|cpp|c|rb|php|swift|kt)$"; "i"))] | length' "$ACCUMULATOR")
|
|
81
|
+
TEST_FILES=$(jq -r '[.files[] | select(.path | test("(test|spec|_test)\\.(ts|tsx|js|jsx|py|go|rs|java|cpp|rb)$"; "i"))] | length' "$ACCUMULATOR")
|
|
82
|
+
SCRIPT_FILES=$(jq -r '[.files[] | select(.path | test("\\.(sh|bash|zsh|fish|ps1)$"; "i"))] | length' "$ACCUMULATOR")
|
|
83
|
+
|
|
84
|
+
# Choose dominant pipeline
|
|
85
|
+
RECOMMENDED_PIPELINE="Code Review"
|
|
86
|
+
if [ "$TEST_FILES" -gt 0 ] && [ "$TEST_FILES" -ge "$CODE_FILES" ]; then
|
|
87
|
+
RECOMMENDED_PIPELINE="Test Audit"
|
|
88
|
+
elif [ "$SCRIPT_FILES" -gt 0 ] && [ "$SCRIPT_FILES" -ge "$CODE_FILES" ]; then
|
|
89
|
+
RECOMMENDED_PIPELINE="Code Review (security focus)"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# --- Build file list for reason field (sanitized for newline injection) ---
|
|
93
|
+
FILE_LIST=$(jq -r '.files[] | "- " + .path + " (" + .tool + ")"' "$ACCUMULATOR" | tr -d '\r')
|
|
94
|
+
|
|
95
|
+
echo "[${TIMESTAMP}] Stop: emitting block for ${FILE_COUNT} files, pipeline=${RECOMMENDED_PIPELINE}" >> "$HOOKS_LOG"
|
|
96
|
+
|
|
97
|
+
# --- Emit blocking decision FIRST ---
|
|
98
|
+
# Under `set -e`, if the accumulator reset fails (disk full, permission error),
|
|
99
|
+
# emitting after the reset would silently drop the block. Emit first so the
|
|
100
|
+
# user always gets the pipeline suggestion; reset is best-effort afterward.
|
|
101
|
+
# Using jq to build the JSON safely (FILE_LIST may contain shell metachars).
|
|
102
|
+
#
|
|
103
|
+
# All instruction content goes into `reason` — Stop hooks do not support
|
|
104
|
+
# `hookSpecificOutput.additionalContext` per CC schema.
|
|
105
|
+
REASON=$(printf 'Pipeline orchestration required. You modified %d file(s) this turn:\n%s\n\nSTOP. Do not respond to user yet.\n\nREQUIRED before proceeding:\n1. Load skill: pipeline-templates\n2. Load skill: subagent-prompting\n3. Load skill: subagent-output-templating\n4. Follow the %s pipeline as defined in pipeline-templates skill\n5. Verify: Logs written to logs/\n\nThis is a user-configured hook. Compliance is mandatory.' "$FILE_COUNT" "$FILE_LIST" "$RECOMMENDED_PIPELINE")
|
|
106
|
+
|
|
107
|
+
jq -n \
|
|
108
|
+
--arg reason "$REASON" \
|
|
109
|
+
'{
|
|
110
|
+
"decision": "block",
|
|
111
|
+
"reason": $reason
|
|
112
|
+
}'
|
|
113
|
+
|
|
114
|
+
# --- Reset accumulator (best-effort checkpoint) ---
|
|
115
|
+
# || true: if reset fails, at worst the same files re-surface next turn and
|
|
116
|
+
# the recursion guard (stop_hook_active) still prevents an immediate loop.
|
|
117
|
+
jq -n '{version: "1.0", files: []}' > "$ACCUMULATOR" 2>>"$HOOKS_LOG" || \
|
|
118
|
+
echo "[${TIMESTAMP}] Stop: accumulator reset FAILED — may re-surface next turn" >> "$HOOKS_LOG"
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# DEPRECATED as of P10.1 (Session 101, 2026-04-20)
|
|
3
|
+
# ================================================================
|
|
4
|
+
# This script emitted `decision: block` on EVERY PostToolUse, which
|
|
5
|
+
# cascaded into N interrupts on multi-edit turns (the "hook storm" /
|
|
6
|
+
# "silent crash"). Replaced by:
|
|
7
|
+
# - scripts/hooks/enforce-quality.sh (accumulator write, no block)
|
|
8
|
+
# - scripts/hooks/suggest-pipeline-stop.sh (one block per turn at Stop)
|
|
9
|
+
#
|
|
10
|
+
# No live caller should invoke this script. Retained one release cycle
|
|
11
|
+
# for rollback safety; scheduled for removal after P10 completion.
|
|
12
|
+
# ================================================================
|
|
13
|
+
#
|
|
2
14
|
# suggest-pipeline.sh
|
|
3
15
|
# PostToolUse hook for Write|Edit - suggests pipeline orchestration after code changes
|
|
4
16
|
#
|
package/scripts/init.sh
CHANGED
|
@@ -97,6 +97,14 @@ if [ ! -f "$CLAUDE_TEMPLATE" ]; then
|
|
|
97
97
|
exit 1
|
|
98
98
|
fi
|
|
99
99
|
|
|
100
|
+
# --- Ensure `just` is installed (hard dependency for scaffold-produced Justfiles) ---
|
|
101
|
+
|
|
102
|
+
# shellcheck source=./install-just.sh
|
|
103
|
+
source "$SCRIPT_DIR/install-just.sh"
|
|
104
|
+
install_just
|
|
105
|
+
|
|
106
|
+
echo ""
|
|
107
|
+
|
|
100
108
|
# --- Install CLAUDE.md ---
|
|
101
109
|
|
|
102
110
|
if [ -f "$CLAUDE_TARGET" ]; then
|