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
139 changes: 0 additions & 139 deletions .claude/agents/tech-writer.md

This file was deleted.

15 changes: 7 additions & 8 deletions .claude/hooks/post-edit-dale.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
# Output: JSON with context message for Claude (stdout on exit 0)

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Read one field from the hook's JSON input. Uses node (always present in this repo); jq is not installed everywhere.
read_field() {
echo "$INPUT" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{const v=process.argv[1].split(".").reduce((o,k)=>o?.[k],JSON.parse(d));process.stdout.write(v==null?"":String(v))}catch{}})' "$1"
}
TOOL_NAME=$(read_field tool_name)
FILE_PATH=$(read_field tool_input.file_path)

# Only act on Edit or Write tools
if [ "$TOOL_NAME" != "Edit" ] && [ "$TOOL_NAME" != "Write" ]; then
Expand All @@ -30,9 +34,4 @@ if [ "$BASENAME" = "CLAUDE.md" ] || [ "$BASENAME" = "SKILL.md" ] || [ "$BASENAME
fi

# Output a context message that Claude will see
jq -n --arg file "$FILE_PATH" '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
message: ("You just edited " + $file + ". Run /dale " + $file + " to check for dale linting issues.")
}
}'
node -e 'const f=process.argv[1];console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",message:"You just edited "+f+". Run /dale "+f+" to check for dale linting issues."}}))' "$FILE_PATH"
25 changes: 25 additions & 0 deletions .claude/hooks/post-edit-record.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# PostToolUse hook (Edit|Write): remember which docs this session edited, so the Stop hook
# checks only those and never pushes Claude to rewrite someone else's draft on the branch.
# Appends the repo-relative path to "$TMPDIR/nwx-quality-files-<session>". Never blocks.

INPUT=$(cat)
node -e '
let d = "";
process.stdin.on("data", c => (d += c)).on("end", () => {
try {
const j = JSON.parse(d);
const file = j.tool_input && j.tool_input.file_path;
if (!["Edit", "Write"].includes(j.tool_name) || !file) return;
const path = require("path"), fs = require("fs"), os = require("os");
const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
const rel = path.relative(root, path.resolve(root, file)).split(path.sep).join("/");
if (!/^docs\/.*\.mdx?$/.test(rel) || /(^|\/)(CLAUDE|SKILL)\.md$/.test(rel)) return;
const session = String(j.session_id || "default");
const list = path.join(process.env.TMPDIR || os.tmpdir(), "nwx-quality-files-" + session);
const seen = fs.existsSync(list) ? fs.readFileSync(list, "utf8").split("\n") : [];
if (!seen.includes(rel)) fs.appendFileSync(list, rel + "\n");
} catch {}
});
' <<< "$INPUT" 2>/dev/null
exit 0
15 changes: 7 additions & 8 deletions .claude/hooks/post-vale-recheck.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
# Input: JSON on stdin with tool_name and tool_input fields

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Read one field from the hook's JSON input. Uses node (always present in this repo); jq is not installed everywhere.
read_field() {
echo "$INPUT" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{const v=process.argv[1].split(".").reduce((o,k)=>o?.[k],JSON.parse(d));process.stdout.write(v==null?"":String(v))}catch{}})' "$1"
}
TOOL_NAME=$(read_field tool_name)
COMMAND=$(read_field tool_input.command)

# Only act on Bash tool
if [ "$TOOL_NAME" != "Bash" ]; then
Expand All @@ -20,9 +24,4 @@ if ! echo "$COMMAND" | grep -q "^vale "; then
fi

# Remind to check if issues remain
jq -n '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
message: "Vale run complete. If issues were found, fix them and re-run vale until zero errors remain."
}
}'
node -e 'console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",message:"Vale run complete. If issues were found, fix them and re-run vale until zero errors remain."}}))'
58 changes: 58 additions & 0 deletions .claude/hooks/stop-quality-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
# Stop hook: before Claude finishes, run the pre-PR style check (Vale, Dale, AI-isms) on the
# docs THIS SESSION edited (recorded by post-edit-record.sh): new pages whole, existing pages on
# their changed lines. Other drafts on the branch are not this session's to rewrite, so they are
# left alone. If anything is flagged, exit 2 hands the findings back to Claude to fix. After 3
# rounds it lets go; the pre-commit hook is the hard gate and checks everything that is staged.

[ -n "$NWX_QUALITY_CHILD" ] && exit 0
# CI has its own backstop (Vale Auto-Fix), and its jobs would run model calls beyond their scope.
[ -n "$GITHUB_ACTIONS" ] && exit 0
INPUT=$(cat)
# Parse with node, which the repo already requires; jq is not installed everywhere.
SESSION=$(echo "$INPUT" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{process.stdout.write(String(JSON.parse(d).session_id||""))}catch{}})' 2>/dev/null)
SESSION=${SESSION:-default}
COUNTER="${TMPDIR:-/tmp}/nwx-quality-$SESSION"
ROUNDS=$(cat "$COUNTER" 2>/dev/null || echo 0)

cd "${CLAUDE_PROJECT_DIR:-.}" || exit 0
[ -f scripts/quality/check.mjs ] || exit 0
# Without installed dependencies the check cannot load, so there is nothing to run.
[ -d node_modules/js-yaml ] && [ -d node_modules/@anthropic-ai/sdk ] || exit 0

# Only the files this session edited. None edited, nothing to check.
LIST="${TMPDIR:-/tmp}/nwx-quality-files-$SESSION"
if [ ! -s "$LIST" ]; then
rm -f "$COUNTER"
exit 0
fi

# Unresolved draft markers in those files: tell the user, never block.
node scripts/quality/todo-check.mjs --files $(cat "$LIST") >&2 || true

OUTPUT=$(node scripts/quality/check.mjs --base origin/dev --only "$LIST" --vale-optional --table 2>&1)
CODE=$?
if [ "$CODE" -eq 0 ]; then
rm -f "$COUNTER"
exit 0
fi
# Exit 1 means style findings. Any other code is a crash of the check itself, which is not Claude's to fix.
if [ "$CODE" -ne 1 ]; then
echo "The style check could not run (exit $CODE); skipping it this turn." >&2
echo "$OUTPUT" | tail -5 >&2
exit 0
fi

if [ "$ROUNDS" -ge 3 ]; then
rm -f "$COUNTER"
exit 0
fi
echo $((ROUNDS + 1)) > "$COUNTER"

{
echo "The AI-isms check flagged documentation you wrote or edited (round $((ROUNDS + 1)) of 3):"
echo "$OUTPUT"
echo ""
echo "Rewrite the flagged lines in plain, specific wording without changing what they say. Do not add new filler. Then finish."
} >&2
exit 2
Loading
Loading