oh-my-customcode 1.1.54 → 1.1.56

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.
Files changed (42) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +1 -1
  4. package/templates/.claude/hooks/hooks.json +19 -19
  5. package/templates/.claude/hooks/scripts/agent-start-recorder.sh +5 -5
  6. package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +4 -4
  7. package/templates/.claude/hooks/scripts/audit-log.sh +6 -6
  8. package/templates/.claude/hooks/scripts/auto-continue-guard.sh +1 -1
  9. package/templates/.claude/hooks/scripts/auto-dev-token-summary.sh +6 -6
  10. package/templates/.claude/hooks/scripts/auto-dev-token-tracker.sh +6 -6
  11. package/templates/.claude/hooks/scripts/content-hash-validator.sh +4 -4
  12. package/templates/.claude/hooks/scripts/context-budget-advisor.sh +3 -3
  13. package/templates/.claude/hooks/scripts/cost-cap-advisor.sh +6 -6
  14. package/templates/.claude/hooks/scripts/cwd-change-detector.sh +3 -3
  15. package/templates/.claude/hooks/scripts/destructive-git-guard.sh +2 -2
  16. package/templates/.claude/hooks/scripts/eval-core-batch-save.sh +2 -2
  17. package/templates/.claude/hooks/scripts/feedback-collector.sh +7 -7
  18. package/templates/.claude/hooks/scripts/file-change-validator.sh +4 -4
  19. package/templates/.claude/hooks/scripts/git-delegation-guard.sh +3 -3
  20. package/templates/.claude/hooks/scripts/model-escalation-advisor.sh +4 -4
  21. package/templates/.claude/hooks/scripts/omcustom-auto-update.sh +6 -6
  22. package/templates/.claude/hooks/scripts/playwright-compress.sh +4 -4
  23. package/templates/.claude/hooks/scripts/plugin-cache-check.sh +2 -2
  24. package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +26 -3
  25. package/templates/.claude/hooks/scripts/rtk-intercept.sh +9 -9
  26. package/templates/.claude/hooks/scripts/rule-deletion-guard.sh +5 -5
  27. package/templates/.claude/hooks/scripts/schema-validator.sh +10 -10
  28. package/templates/.claude/hooks/scripts/secret-filter.sh +4 -4
  29. package/templates/.claude/hooks/scripts/session-autofix-prompt.sh +3 -3
  30. package/templates/.claude/hooks/scripts/session-autofix.sh +1 -1
  31. package/templates/.claude/hooks/scripts/session-env-check.sh +1 -1
  32. package/templates/.claude/hooks/scripts/session-reflection.sh +15 -15
  33. package/templates/.claude/hooks/scripts/skill-extractor-analyzer.sh +2 -2
  34. package/templates/.claude/hooks/scripts/stale-todo-scanner.sh +1 -1
  35. package/templates/.claude/hooks/scripts/stall-detection-advisor.sh +11 -11
  36. package/templates/.claude/hooks/scripts/stop-console-audit.sh +1 -1
  37. package/templates/.claude/hooks/scripts/stuck-detector.sh +81 -25
  38. package/templates/.claude/hooks/scripts/subagent-failure-advisor.sh +36 -0
  39. package/templates/.claude/hooks/scripts/task-outcome-recorder.sh +8 -8
  40. package/templates/.claude/hooks/scripts/task-state-precompact.sh +1 -1
  41. package/templates/.claude/settings.json +19 -19
  42. package/templates/manifest.json +1 -1
package/dist/cli/index.js CHANGED
@@ -243,7 +243,7 @@ var init_package = __esm(() => {
243
243
  workspaces: [
244
244
  "packages/*"
245
245
  ],
246
- version: "1.1.54",
246
+ version: "1.1.56",
247
247
  description: "Batteries-included agent harness for Claude Code",
248
248
  type: "module",
249
249
  bin: {
package/dist/index.js CHANGED
@@ -2535,7 +2535,7 @@ var package_default = {
2535
2535
  workspaces: [
2536
2536
  "packages/*"
2537
2537
  ],
2538
- version: "1.1.54",
2538
+ version: "1.1.56",
2539
2539
  description: "Batteries-included agent harness for Claude Code",
2540
2540
  type: "module",
2541
2541
  bin: {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "workspaces": [
4
4
  "packages/*"
5
5
  ],
6
- "version": "1.1.54",
6
+ "version": "1.1.56",
7
7
  "description": "Batteries-included agent harness for Claude Code",
8
8
  "type": "module",
9
9
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "hooks": [
18
18
  {
19
19
  "type": "command",
20
- "command": "#!/bin/bash\ninput=$(cat)\ncmd=$(echo \"$input\" | jq -r '.tool_input.command // \"\"')\n\n# Block dev servers that aren't run in tmux\necho '[Hook] BLOCKED: Dev server must run in tmux for log access' >&2\necho '[Hook] Use this command instead:' >&2\necho \"[Hook] tmux new-session -d -s dev 'npm run dev'\" >&2\necho '[Hook] Then: tmux attach -t dev' >&2\nexit 1"
20
+ "command": "#!/bin/bash\ninput=$(cat)\ncmd=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.command // \"\"')\n\n# Block dev servers that aren't run in tmux\necho '[Hook] BLOCKED: Dev server must run in tmux for log access' >&2\necho '[Hook] Use this command instead:' >&2\necho \"[Hook] tmux new-session -d -s dev 'npm run dev'\" >&2\necho '[Hook] Then: tmux attach -t dev' >&2\nexit 1"
21
21
  }
22
22
  ],
23
23
  "description": "Block dev servers outside tmux - ensures you can access logs"
@@ -27,7 +27,7 @@
27
27
  "hooks": [
28
28
  {
29
29
  "type": "command",
30
- "command": "#!/bin/bash\ninput=$(cat)\nif [ -z \"$TMUX\" ]; then\n echo '[Hook] Consider running in tmux for session persistence' >&2\n echo '[Hook] tmux new -s dev | tmux attach -t dev' >&2\nfi\necho \"$input\""
30
+ "command": "#!/bin/bash\ninput=$(cat)\nif [ -z \"$TMUX\" ]; then\n echo '[Hook] Consider running in tmux for session persistence' >&2\n echo '[Hook] tmux new -s dev | tmux attach -t dev' >&2\nfi\nprintf '%s\\n' \"$input\""
31
31
  }
32
32
  ],
33
33
  "description": "Reminder to use tmux for long-running commands"
@@ -47,7 +47,7 @@
47
47
  "hooks": [
48
48
  {
49
49
  "type": "command",
50
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [[ \"$file_path\" =~ \\.(md|txt)$ ]] && [[ ! \"$file_path\" =~ (README|CLAUDE|AGENT|SKILL)\\.md$ ]]; then\n echo \"[Hook] BLOCKED: Unnecessary documentation file creation\" >&2\n echo \"[Hook] File: $file_path\" >&2\n echo \"[Hook] Use README.md or AGENT.md for documentation instead\" >&2\n exit 1\nfi\n\necho \"$input\""
50
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [[ \"$file_path\" =~ \\.(md|txt)$ ]] && [[ ! \"$file_path\" =~ (README|CLAUDE|AGENT|SKILL)\\.md$ ]]; then\n echo \"[Hook] BLOCKED: Unnecessary documentation file creation\" >&2\n echo \"[Hook] File: $file_path\" >&2\n echo \"[Hook] Use README.md or AGENT.md for documentation instead\" >&2\n exit 1\nfi\n\nprintf '%s\\n' \"$input\""
51
51
  }
52
52
  ],
53
53
  "description": "Block creation of random .md files - keeps docs consolidated"
@@ -117,7 +117,7 @@
117
117
  "hooks": [
118
118
  {
119
119
  "type": "command",
120
- "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.tool_input.subagent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.tool_input.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.tool_input.description // \"\"' | head -c 40)\nresume=$(echo \"$input\" | jq -r '.tool_input.resume // empty')\nif [ -n \"$resume\" ]; then\n echo \"─── [Resume] ${agent_type}:${model} | ${desc} ───\" >&2\nelse\n echo \"─── [Spawn] ${agent_type}:${model} | ${desc} ───\" >&2\nfi\necho \"$input\""
120
+ "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.subagent_type // \"unknown\"')\nmodel=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.model // \"inherit\"')\ndesc=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.description // \"\"' | head -c 40)\nresume=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.resume // empty')\nif [ -n \"$resume\" ]; then\n echo \"─── [Resume] ${agent_type}:${model} | ${desc} ───\" >&2\nelse\n echo \"─── [Spawn] ${agent_type}:${model} | ${desc} ───\" >&2\nfi\nprintf '%s\\n' \"$input\""
121
121
  },
122
122
  {
123
123
  "type": "command",
@@ -255,7 +255,7 @@
255
255
  "hooks": [
256
256
  {
257
257
  "type": "command",
258
- "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.agent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' | head -c 40)\necho \"─── [SubagentStart] ${agent_type}:${model} | ${desc} ───\" >&2\necho \"$input\""
258
+ "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(printf '%s\\n' \"$input\" | jq -r '.agent_type // \"unknown\"')\nmodel=$(printf '%s\\n' \"$input\" | jq -r '.model // \"inherit\"')\ndesc=$(printf '%s\\n' \"$input\" | jq -r '.description // \"\"' | head -c 40)\necho \"─── [SubagentStart] ${agent_type}:${model} | ${desc} ───\" >&2\nprintf '%s\\n' \"$input\""
259
259
  },
260
260
  {
261
261
  "type": "command",
@@ -286,11 +286,11 @@
286
286
  "command": "bash .claude/hooks/scripts/r007-r008-drift-advisor.sh"
287
287
  },
288
288
  {
289
- "type": "prompt",
290
- "prompt": "A background subagent just completed. If it FAILED, do NOT auto-continue — report the failure and wait for user input."
289
+ "type": "command",
290
+ "command": "bash .claude/hooks/scripts/subagent-failure-advisor.sh"
291
291
  }
292
292
  ],
293
- "description": "Record agent outcomes + auto-continue workflow + R007/R008 drift advisory on subagent completion (autonomous-loop re-entry, #1545)"
293
+ "description": "Record agent outcomes + auto-continue workflow + R007/R008 drift advisory + failure advisory on subagent completion (autonomous-loop re-entry, #1545; #1631 replaced the self-referential prompt-type judge hook with an advisory-only command hook)"
294
294
  }
295
295
  ],
296
296
  "CwdChanged": [
@@ -323,7 +323,7 @@
323
323
  "hooks": [
324
324
  {
325
325
  "type": "command",
326
- "command": "#!/bin/bash\ninput=$(cat)\nmsg=$(echo \"$input\" | jq -r '.message // \"\"' 2>/dev/null)\nif [ -n \"$msg\" ]; then\n echo \"[Hook] Notification: $msg\" >&2\nfi\necho \"$input\""
326
+ "command": "#!/bin/bash\ninput=$(cat)\nmsg=$(printf '%s\\n' \"$input\" | jq -r '.message // \"\"' 2>/dev/null)\nif [ -n \"$msg\" ]; then\n echo \"[Hook] Notification: $msg\" >&2\nfi\nprintf '%s\\n' \"$input\""
327
327
  }
328
328
  ],
329
329
  "description": "Display notifications for long-running operation completions"
@@ -335,7 +335,7 @@
335
335
  "hooks": [
336
336
  {
337
337
  "type": "command",
338
- "command": "#!/bin/bash\ninput=$(cat)\nteammate=$(echo \"$input\" | jq -r '.teammate_id // \"unknown\"' 2>/dev/null)\necho \"[Hook] TeammateIdle: $teammate — available for reassignment\" >&2\necho \"$input\""
338
+ "command": "#!/bin/bash\ninput=$(cat)\nteammate=$(printf '%s\\n' \"$input\" | jq -r '.teammate_id // \"unknown\"' 2>/dev/null)\necho \"[Hook] TeammateIdle: $teammate — available for reassignment\" >&2\nprintf '%s\\n' \"$input\""
339
339
  }
340
340
  ],
341
341
  "description": "Track idle Agent Teams members for work reassignment"
@@ -347,7 +347,7 @@
347
347
  "hooks": [
348
348
  {
349
349
  "type": "command",
350
- "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCreated: $task_id — $desc\" >&2\necho \"$input\""
350
+ "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(printf '%s\\n' \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\ndesc=$(printf '%s\\n' \"$input\" | jq -r '.description // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCreated: $task_id — $desc\" >&2\nprintf '%s\\n' \"$input\""
351
351
  }
352
352
  ],
353
353
  "description": "Log task creation events for Agent Teams coordination"
@@ -359,7 +359,7 @@
359
359
  "hooks": [
360
360
  {
361
361
  "type": "command",
362
- "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\nresult=$(echo \"$input\" | jq -r '.result // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCompleted: $task_id — $result\" >&2\necho \"$input\""
362
+ "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(printf '%s\\n' \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\nresult=$(printf '%s\\n' \"$input\" | jq -r '.result // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCompleted: $task_id — $result\" >&2\nprintf '%s\\n' \"$input\""
363
363
  }
364
364
  ],
365
365
  "description": "Log task completion events for Agent Teams coordination"
@@ -395,7 +395,7 @@
395
395
  "hooks": [
396
396
  {
397
397
  "type": "command",
398
- "command": "#!/bin/bash\ninput=$(cat)\ncmd=$(echo \"$input\" | jq -r '.tool_input.command')\n\nif echo \"$cmd\" | grep -qE 'gh pr create'; then\n output=$(echo \"$input\" | jq -r '.tool_output.output // \"\"')\n pr_url=$(echo \"$output\" | grep -oE 'https://github.com/[^/]+/[^/]+/pull/[0-9]+')\n \n if [ -n \"$pr_url\" ]; then\n echo \"[Hook] PR created: $pr_url\" >&2\n echo \"[Hook] Checking GitHub Actions status...\" >&2\n repo=$(echo \"$pr_url\" | sed -E 's|https://github.com/([^/]+/[^/]+)/pull/[0-9]+|\\1|')\n pr_num=$(echo \"$pr_url\" | sed -E 's|.*/pull/([0-9]+)|\\1|')\n echo \"[Hook] To review PR: gh pr review $pr_num --repo $repo\" >&2\n fi\nfi\n\necho \"$input\""
398
+ "command": "#!/bin/bash\ninput=$(cat)\ncmd=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.command')\n\nif echo \"$cmd\" | grep -qE 'gh pr create'; then\n output=$(printf '%s\\n' \"$input\" | jq -r '.tool_output.output // \"\"')\n pr_url=$(echo \"$output\" | grep -oE 'https://github.com/[^/]+/[^/]+/pull/[0-9]+')\n \n if [ -n \"$pr_url\" ]; then\n echo \"[Hook] PR created: $pr_url\" >&2\n echo \"[Hook] Checking GitHub Actions status...\" >&2\n repo=$(echo \"$pr_url\" | sed -E 's|https://github.com/([^/]+/[^/]+)/pull/[0-9]+|\\1|')\n pr_num=$(echo \"$pr_url\" | sed -E 's|.*/pull/([0-9]+)|\\1|')\n echo \"[Hook] To review PR: gh pr review $pr_num --repo $repo\" >&2\n fi\nfi\n\nprintf '%s\\n' \"$input\""
399
399
  }
400
400
  ],
401
401
  "description": "Log PR URL and provide review command after PR creation"
@@ -405,7 +405,7 @@
405
405
  "hooks": [
406
406
  {
407
407
  "type": "command",
408
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n if command -v prettier >/dev/null 2>&1; then\n prettier --write \"$file_path\" 2>&1 | head -5 >&2\n fi\nfi\n\necho \"$input\""
408
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n if command -v prettier >/dev/null 2>&1; then\n prettier --write \"$file_path\" 2>&1 | head -5 >&2\n fi\nfi\n\nprintf '%s\\n' \"$input\""
409
409
  }
410
410
  ],
411
411
  "description": "Auto-format JS/TS files with Prettier after edits"
@@ -415,7 +415,7 @@
415
415
  "hooks": [
416
416
  {
417
417
  "type": "command",
418
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n dir=$(dirname \"$file_path\")\n project_root=\"$dir\"\n while [ \"$project_root\" != \"/\" ] && [ ! -f \"$project_root/package.json\" ]; do\n project_root=$(dirname \"$project_root\")\n done\n \n if [ -f \"$project_root/tsconfig.json\" ]; then\n cd \"$project_root\" && npx tsc --noEmit --pretty false 2>&1 | grep \"$file_path\" | head -10 >&2 || true\n fi\nfi\n\necho \"$input\"",
418
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n dir=$(dirname \"$file_path\")\n project_root=\"$dir\"\n while [ \"$project_root\" != \"/\" ] && [ ! -f \"$project_root/package.json\" ]; do\n project_root=$(dirname \"$project_root\")\n done\n \n if [ -f \"$project_root/tsconfig.json\" ]; then\n cd \"$project_root\" && npx tsc --noEmit --pretty false 2>&1 | grep \"$file_path\" | head -10 >&2 || true\n fi\nfi\n\nprintf '%s\\n' \"$input\"",
419
419
  "continueOnBlock": true
420
420
  }
421
421
  ],
@@ -426,7 +426,7 @@
426
426
  "hooks": [
427
427
  {
428
428
  "type": "command",
429
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n console_logs=$(grep -n \"console\\\\.log\" \"$file_path\" 2>/dev/null || true)\n \n if [ -n \"$console_logs\" ]; then\n echo \"[Hook] WARNING: console.log found in $file_path\" >&2\n echo \"$console_logs\" | head -5 >&2\n echo \"[Hook] Remove console.log before committing\" >&2\n fi\nfi\n\necho \"$input\"",
429
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n console_logs=$(grep -n \"console\\\\.log\" \"$file_path\" 2>/dev/null || true)\n \n if [ -n \"$console_logs\" ]; then\n echo \"[Hook] WARNING: console.log found in $file_path\" >&2\n echo \"$console_logs\" | head -5 >&2\n echo \"[Hook] Remove console.log before committing\" >&2\n fi\nfi\n\nprintf '%s\\n' \"$input\"",
430
430
  "continueOnBlock": true
431
431
  }
432
432
  ],
@@ -437,7 +437,7 @@
437
437
  "hooks": [
438
438
  {
439
439
  "type": "command",
440
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n if command -v gofmt >/dev/null 2>&1; then\n gofmt -w \"$file_path\" 2>&1 >&2\n fi\nfi\n\necho \"$input\""
440
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n if command -v gofmt >/dev/null 2>&1; then\n gofmt -w \"$file_path\" 2>&1 >&2\n fi\nfi\n\nprintf '%s\\n' \"$input\""
441
441
  }
442
442
  ],
443
443
  "description": "Auto-format Go files with gofmt after edits"
@@ -447,7 +447,7 @@
447
447
  "hooks": [
448
448
  {
449
449
  "type": "command",
450
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n # Format with ruff\n if command -v ruff >/dev/null 2>&1; then\n ruff format --quiet \"$file_path\" 2>&1 >&2\n ruff check --fix --quiet \"$file_path\" 2>&1 >&2 || true\n echo \"[Hook] Python formatted with ruff\" >&2\n fi\nfi\n\necho \"$input\"",
450
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n # Format with ruff\n if command -v ruff >/dev/null 2>&1; then\n ruff format --quiet \"$file_path\" 2>&1 >&2\n ruff check --fix --quiet \"$file_path\" 2>&1 >&2 || true\n echo \"[Hook] Python formatted with ruff\" >&2\n fi\nfi\n\nprintf '%s\\n' \"$input\"",
451
451
  "continueOnBlock": true
452
452
  }
453
453
  ],
@@ -458,7 +458,7 @@
458
458
  "hooks": [
459
459
  {
460
460
  "type": "command",
461
- "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n # Type check with ty\n if command -v ty >/dev/null 2>&1; then\n dir=$(dirname \"$file_path\")\n project_root=\"$dir\"\n while [ \"$project_root\" != \"/\" ] && [ ! -f \"$project_root/pyproject.toml\" ]; do\n project_root=$(dirname \"$project_root\")\n done\n \n if [ -f \"$project_root/pyproject.toml\" ]; then\n cd \"$project_root\" && ty check \"$file_path\" 2>&1 | head -10 >&2 || true\n else\n ty check \"$file_path\" 2>&1 | head -10 >&2 || true\n fi\n fi\nfi\n\necho \"$input\"",
461
+ "command": "#!/bin/bash\ninput=$(cat)\nfile_path=$(printf '%s\\n' \"$input\" | jq -r '.tool_input.file_path // \"\"')\n\nif [ -n \"$file_path\" ] && [ -f \"$file_path\" ]; then\n # Type check with ty\n if command -v ty >/dev/null 2>&1; then\n dir=$(dirname \"$file_path\")\n project_root=\"$dir\"\n while [ \"$project_root\" != \"/\" ] && [ ! -f \"$project_root/pyproject.toml\" ]; do\n project_root=$(dirname \"$project_root\")\n done\n \n if [ -f \"$project_root/pyproject.toml\" ]; then\n cd \"$project_root\" && ty check \"$file_path\" 2>&1 | head -10 >&2 || true\n else\n ty check \"$file_path\" 2>&1 | head -10 >&2 || true\n fi\n fi\nfi\n\nprintf '%s\\n' \"$input\"",
462
462
  "continueOnBlock": true
463
463
  }
464
464
  ],
@@ -10,9 +10,9 @@ command -v jq >/dev/null 2>&1 || { cat; exit 0; }
10
10
 
11
11
  input=$(cat)
12
12
 
13
- agent_type=$(echo "$input" | jq -r '.agent_type // "unknown"')
14
- model=$(echo "$input" | jq -r '.model // "inherit"')
15
- description=$(echo "$input" | jq -r '.description // ""' | head -c 80)
13
+ agent_type=$(printf '%s\n' "$input" | jq -r '.agent_type // "unknown"')
14
+ model=$(printf '%s\n' "$input" | jq -r '.model // "inherit"')
15
+ description=$(printf '%s\n' "$input" | jq -r '.description // ""' | head -c 80)
16
16
 
17
17
  AGENT_START_FILE="/tmp/.claude-agent-starts-${PPID}"
18
18
 
@@ -25,7 +25,7 @@ entry=$(jq -cn \
25
25
  --arg desc "$description" \
26
26
  '{start_epoch: $ts, agent_type: $agent, model: $model, description: $desc}')
27
27
 
28
- echo "$entry" >> "$AGENT_START_FILE"
28
+ printf '%s\n' "$entry" >> "$AGENT_START_FILE"
29
29
 
30
30
  # Ring buffer: 50 max
31
31
  if [ -f "$AGENT_START_FILE" ]; then
@@ -36,5 +36,5 @@ if [ -f "$AGENT_START_FILE" ]; then
36
36
  fi
37
37
  fi
38
38
 
39
- echo "$input"
39
+ printf '%s\n' "$input"
40
40
  exit 0
@@ -21,7 +21,7 @@ ENV_STATUS="/tmp/.claude-env-status-${PPID}"
21
21
  if [ -f "$ENV_STATUS" ]; then
22
22
  teams_status=$(grep "agent_teams=" "$ENV_STATUS" 2>/dev/null | cut -d= -f2 || echo "unknown")
23
23
  if [ "$teams_status" != "enabled" ]; then
24
- echo "$input"
24
+ printf '%s\n' "$input"
25
25
  exit 0
26
26
  fi
27
27
  fi
@@ -43,8 +43,8 @@ if [ -f "$RELEASE_PLAN" ]; then
43
43
  fi
44
44
 
45
45
  # Extract task info from input
46
- agent_type=$(echo "$input" | jq -r '.tool_input.subagent_type // "unknown"')
47
- prompt_preview=$(echo "$input" | jq -r '.tool_input.description // ""' | head -c 60)
46
+ agent_type=$(printf '%s\n' "$input" | jq -r '.tool_input.subagent_type // "unknown"')
47
+ prompt_preview=$(printf '%s\n' "$input" | jq -r '.tool_input.description // ""' | head -c 60)
48
48
 
49
49
  # Session-scoped counter using parent PID as session identifier
50
50
  COUNTER_FILE="/tmp/.claude-task-count-${PPID}"
@@ -77,5 +77,5 @@ elif [ "$COUNT" -ge 2 ]; then
77
77
  fi
78
78
 
79
79
  # Always pass through -- advisory only, never blocks
80
- echo "$input"
80
+ printf '%s\n' "$input"
81
81
  exit 0
@@ -14,11 +14,11 @@ command -v jq >/dev/null 2>&1 || exit 0
14
14
  input=$(cat)
15
15
 
16
16
  # Extract fields from hook input
17
- tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
18
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.command // ""' | head -c 200)
19
- agent_type=$(echo "$input" | jq -r '.agent_type // "unknown"')
20
- model=$(echo "$input" | jq -r '.model // "unknown"')
21
- is_error=$(echo "$input" | jq -r '.tool_output.is_error // false')
17
+ tool_name=$(printf '%s\n' "$input" | jq -r '.tool_name // "unknown"')
18
+ file_path=$(printf '%s\n' "$input" | jq -r '.tool_input.file_path // .tool_input.command // ""' | head -c 200)
19
+ agent_type=$(printf '%s\n' "$input" | jq -r '.agent_type // "unknown"')
20
+ model=$(printf '%s\n' "$input" | jq -r '.model // "unknown"')
21
+ is_error=$(printf '%s\n' "$input" | jq -r '.tool_output.is_error // false')
22
22
  timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
23
23
 
24
24
  # Determine outcome
@@ -55,7 +55,7 @@ if [ -f "$AUDIT_LOG" ]; then
55
55
  fi
56
56
 
57
57
  # Pass through
58
- echo "$input"
58
+ printf '%s\n' "$input"
59
59
  HOOK_END=$(date +%s%N 2>/dev/null || echo 0)
60
60
  if [ "$HOOK_START" != "0" ] && [ "$HOOK_END" != "0" ]; then
61
61
  HOOK_MS=$(( (HOOK_END - HOOK_START) / 1000000 ))
@@ -29,5 +29,5 @@ if [ "$count" -ge 4 ]; then
29
29
  echo '[AutoContinue] SAFETY: auto-continue limit (3) reached. Pausing.' >&2
30
30
  fi
31
31
 
32
- echo "$input"
32
+ printf '%s\n' "$input"
33
33
  exit 0
@@ -32,7 +32,7 @@ summary=$(jq -s -r '
32
32
  [ "$summary" = "[]" ] && exit 0
33
33
 
34
34
  # Totals
35
- totals=$(echo "$summary" | jq -r '
35
+ totals=$(printf '%s\n' "$summary" | jq -r '
36
36
  {
37
37
  total_calls: (map(.calls) | add),
38
38
  total_in: (map(.tokens_in) | add),
@@ -40,10 +40,10 @@ totals=$(echo "$summary" | jq -r '
40
40
  grand: (map(.total) | add)
41
41
  }
42
42
  ')
43
- total_calls=$(echo "$totals" | jq -r '.total_calls')
44
- total_in=$(echo "$totals" | jq -r '.total_in')
45
- total_out=$(echo "$totals" | jq -r '.total_out')
46
- grand=$(echo "$totals" | jq -r '.grand')
43
+ total_calls=$(printf '%s\n' "$totals" | jq -r '.total_calls')
44
+ total_in=$(printf '%s\n' "$totals" | jq -r '.total_in')
45
+ total_out=$(printf '%s\n' "$totals" | jq -r '.total_out')
46
+ grand=$(printf '%s\n' "$totals" | jq -r '.grand')
47
47
 
48
48
  {
49
49
  echo ""
@@ -52,7 +52,7 @@ grand=$(echo "$totals" | jq -r '.grand')
52
52
  echo ""
53
53
  printf "| %-32s | %5s | %10s | %10s | %10s |\n" "Phase" "Calls" "Tokens In" "Tokens Out" "Total"
54
54
  printf "| %-32s | %5s | %10s | %10s | %10s |\n" "--------------------------------" "-----" "----------" "----------" "----------"
55
- echo "$summary" | jq -r '.[] | "\(.phase)\t\(.calls)\t\(.tokens_in)\t\(.tokens_out)\t\(.total)"' | \
55
+ printf '%s\n' "$summary" | jq -r '.[] | "\(.phase)\t\(.calls)\t\(.tokens_in)\t\(.tokens_out)\t\(.total)"' | \
56
56
  while IFS=$'\t' read -r phase calls tin tout tot; do
57
57
  phase_trunc=$(printf "%s" "$phase" | head -c 32)
58
58
  printf "| %-32s | %5s | %10s | %10s | %10s |\n" "$phase_trunc" "$calls" "$tin" "$tout" "$tot"
@@ -9,7 +9,7 @@ set -euo pipefail
9
9
 
10
10
  # Always pass through input no matter what
11
11
  input=$(cat)
12
- trap 'echo "$input"' EXIT
12
+ trap 'printf "%s\n" "$input"' EXIT
13
13
 
14
14
  # Gate: only active during auto-dev pipeline
15
15
  PIPELINE_STATE="/tmp/.claude-pipeline-auto-dev-${PPID}.json"
@@ -21,10 +21,10 @@ command -v jq >/dev/null 2>&1 || exit 0
21
21
  LOG_FILE="/tmp/auto-dev-spend-${PPID}.json"
22
22
 
23
23
  # Extract fields (PostToolUse Agent shape)
24
- agent_type=$(echo "$input" | jq -r '.tool_input.subagent_type // "unknown"' 2>/dev/null || echo "unknown")
25
- description=$(echo "$input" | jq -r '.tool_input.description // ""' 2>/dev/null || echo "")
26
- prompt_text=$(echo "$input" | jq -r '.tool_input.prompt // ""' 2>/dev/null || echo "")
27
- output_text=$(echo "$input" | jq -r '.tool_output.output // .tool_output // ""' 2>/dev/null || echo "")
24
+ agent_type=$(printf '%s\n' "$input" | jq -r '.tool_input.subagent_type // "unknown"' 2>/dev/null || echo "unknown")
25
+ description=$(printf '%s\n' "$input" | jq -r '.tool_input.description // ""' 2>/dev/null || echo "")
26
+ prompt_text=$(printf '%s\n' "$input" | jq -r '.tool_input.prompt // ""' 2>/dev/null || echo "")
27
+ output_text=$(printf '%s\n' "$input" | jq -r '.tool_output.output // .tool_output // ""' 2>/dev/null || echo "")
28
28
 
29
29
  # Derive phase from pipeline state (current_phase) or fallback to description prefix
30
30
  phase=$(jq -r '.current_phase // .phase // "unknown"' "$PIPELINE_STATE" 2>/dev/null || echo "unknown")
@@ -52,6 +52,6 @@ entry=$(jq -n -c \
52
52
  --argjson tout "$tokens_out" \
53
53
  '{ts: $ts, phase: $phase, agent: $agent, tokens_in: $tin, tokens_out: $tout}' 2>/dev/null) || exit 0
54
54
 
55
- echo "$entry" >> "$LOG_FILE" 2>/dev/null || true
55
+ printf '%s\n' "$entry" >> "$LOG_FILE" 2>/dev/null || true
56
56
 
57
57
  exit 0
@@ -15,12 +15,12 @@ input=$(cat)
15
15
  # Hash store (PPID-scoped, session-only)
16
16
  HASH_STORE="/tmp/.claude-content-hashes-${PPID}"
17
17
 
18
- tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
18
+ tool_name=$(printf '%s\n' "$input" | jq -r '.tool_name // "unknown"')
19
19
 
20
20
  case "$tool_name" in
21
21
  "Read")
22
22
  # Store content hash for the file that was just read
23
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // ""')
23
+ file_path=$(printf '%s\n' "$input" | jq -r '.tool_input.file_path // ""')
24
24
 
25
25
  if [ -n "$file_path" ] && [ -f "$file_path" ]; then
26
26
  content_hash=$(md5 -q "$file_path" 2>/dev/null || md5sum "$file_path" 2>/dev/null | cut -d' ' -f1 || echo "unknown")
@@ -43,7 +43,7 @@ case "$tool_name" in
43
43
 
44
44
  "Edit")
45
45
  # Validate that file hasn't changed since last Read
46
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // ""')
46
+ file_path=$(printf '%s\n' "$input" | jq -r '.tool_input.file_path // ""')
47
47
 
48
48
  if [ -n "$file_path" ] && [ -f "$HASH_STORE" ] && [ -f "$file_path" ]; then
49
49
  stored_hash=$(grep "\"path\":\"${file_path}\"" "$HASH_STORE" 2>/dev/null | tail -1 | jq -r '.hash // ""' 2>/dev/null || echo "")
@@ -71,5 +71,5 @@ if [ -f "$HASH_STORE" ]; then
71
71
  fi
72
72
 
73
73
  # Always pass through
74
- echo "$input"
74
+ printf '%s\n' "$input"
75
75
  exit 0
@@ -33,7 +33,7 @@ read_count=${read_count:-0}
33
33
  agent_count=${agent_count:-0}
34
34
 
35
35
  # Determine tool type from input
36
- TOOL=$(echo "$input" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
36
+ TOOL=$(printf '%s\n' "$input" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
37
37
  tool_count=$((tool_count + 1))
38
38
 
39
39
  case "$TOOL" in
@@ -95,7 +95,7 @@ if [ "$tool_count" -ge "$THRESHOLD" ] && [ ! -f "$BLOCK_FILE" ]; then
95
95
  HOOK_MS=$(( (HOOK_END - HOOK_START) / 1000000 ))
96
96
  echo "[Hook Perf] $(basename "$0"): ${HOOK_MS}ms" >> "/tmp/.claude-hook-perf-${PPID}.log"
97
97
  fi
98
- echo "$input"
98
+ printf '%s\n' "$input"
99
99
  exit 2
100
100
  fi
101
101
 
@@ -112,7 +112,7 @@ if [ "$tool_count" -gt 0 ] && [ $((tool_count % 50)) -eq 0 ]; then
112
112
  fi
113
113
 
114
114
  # Pass through
115
- echo "$input"
115
+ printf '%s\n' "$input"
116
116
  HOOK_END=$(date +%s%N 2>/dev/null || echo 0)
117
117
  if [ "$HOOK_START" != "0" ] && [ "$HOOK_END" != "0" ]; then
118
118
  HOOK_MS=$(( (HOOK_END - HOOK_START) / 1000000 ))
@@ -15,19 +15,19 @@ COST_CAP="${CLAUDE_COST_CAP:-5.00}"
15
15
 
16
16
  # Check if cost data is available
17
17
  if [ ! -f "$COST_FILE" ]; then
18
- echo "$input"
18
+ printf '%s\n' "$input"
19
19
  exit 0
20
20
  fi
21
21
 
22
22
  # TSV: cost_usd, ctx_pct, timestamp, rl_5h_pct, rl_7d_pct, rl_5h_resets, rl_7d_resets
23
23
  IFS=$'\t' read -r cost_usd ctx_pct timestamp _rl_5h _rl_7d _rl_5h_resets _rl_7d_resets < "$COST_FILE" 2>/dev/null || {
24
- echo "$input"
24
+ printf '%s\n' "$input"
25
25
  exit 0
26
26
  }
27
27
 
28
28
  # Validate cost_usd is a number
29
29
  if ! printf '%f' "$cost_usd" >/dev/null 2>&1; then
30
- echo "$input"
30
+ printf '%s\n' "$input"
31
31
  exit 0
32
32
  fi
33
33
 
@@ -39,7 +39,7 @@ cost_pct=$(echo "scale=0; $cost_usd * 100 / $COST_CAP" | bc 2>/dev/null || echo
39
39
  now=$(date +%s)
40
40
  age=$((now - ${timestamp:-0}))
41
41
  if [ "$age" -gt 60 ]; then
42
- echo "$input"
42
+ printf '%s\n' "$input"
43
43
  exit 0
44
44
  fi
45
45
 
@@ -54,7 +54,7 @@ if [ "$cost_pct" -ge 100 ] && [ "$last_level" != "100" ]; then
54
54
  echo "[Cost Cap] Session cost \$${cost_usd} has reached cap \$${COST_CAP} (${cost_pct}%)" >&2
55
55
  echo "[Cost Cap] Consider wrapping up or increasing CLAUDE_COST_CAP" >&2
56
56
  echo "100" > "$ADVISORY_FILE"
57
- echo "$input"
57
+ printf '%s\n' "$input"
58
58
  exit 2
59
59
  elif [ "$cost_pct" -ge 90 ] && [ "$last_level" != "90" ] && [ "$last_level" != "100" ]; then
60
60
  echo "[Cost Cap] Session cost \$${cost_usd} at 90% of cap \$${COST_CAP}" >&2
@@ -69,5 +69,5 @@ elif [ "$cost_pct" -ge 50 ] && [ -z "$last_level" ]; then
69
69
  fi
70
70
 
71
71
  # Pass through — advisory only
72
- echo "$input"
72
+ printf '%s\n' "$input"
73
73
  exit 0
@@ -3,10 +3,10 @@
3
3
  # Advisory only — provides context hints when cwd changes
4
4
 
5
5
  input=$(cat)
6
- new_cwd=$(echo "$input" | jq -r '.new_cwd // ""' 2>/dev/null)
6
+ new_cwd=$(printf '%s\n' "$input" | jq -r '.new_cwd // ""' 2>/dev/null)
7
7
 
8
8
  if [ -z "$new_cwd" ] || [ ! -d "$new_cwd" ]; then
9
- echo "$input"
9
+ printf '%s\n' "$input"
10
10
  exit 0
11
11
  fi
12
12
 
@@ -33,4 +33,4 @@ if [ -n "$hints" ]; then
33
33
  printf "%b" "$hints" >&2
34
34
  fi
35
35
 
36
- echo "$input"
36
+ printf '%s\n' "$input"
@@ -9,7 +9,7 @@
9
9
 
10
10
  input=$(cat)
11
11
  if command -v jq >/dev/null 2>&1; then
12
- command_text=$(echo "$input" | jq -r '.tool_input.command // ""' 2>/dev/null)
12
+ command_text=$(printf '%s\n' "$input" | jq -r '.tool_input.command // ""' 2>/dev/null)
13
13
  elif command -v python3 >/dev/null 2>&1; then
14
14
  command_text=$(INPUT_JSON="$input" python3 - <<'PY' 2>/dev/null
15
15
  import json, os
@@ -57,4 +57,4 @@ if [ -n "$matched" ]; then
57
57
  fi
58
58
 
59
59
  # Always pass through - this hook is advisory only.
60
- echo "$input"
60
+ printf '%s\n' "$input"
@@ -14,7 +14,7 @@ PPID_FILE="/tmp/.claude-task-outcomes-${PPID}"
14
14
 
15
15
  # Only attempt collection if outcome file exists
16
16
  if [ ! -f "$PPID_FILE" ]; then
17
- echo "$input"
17
+ printf '%s\n' "$input"
18
18
  exit 0
19
19
  fi
20
20
 
@@ -42,5 +42,5 @@ if [ -n "$EVAL_CORE" ]; then
42
42
  fi
43
43
 
44
44
  # Always pass through input and exit 0 (advisory only)
45
- echo "$input"
45
+ printf '%s\n' "$input"
46
46
  exit 0
@@ -8,16 +8,16 @@ set -euo pipefail
8
8
  input=$(cat)
9
9
 
10
10
  # Dependencies check
11
- command -v jq >/dev/null 2>&1 || { echo "$input"; exit 0; }
12
- command -v sqlite3 >/dev/null 2>&1 || { echo "$input"; exit 0; }
11
+ command -v jq >/dev/null 2>&1 || { printf '%s\n' "$input"; exit 0; }
12
+ command -v sqlite3 >/dev/null 2>&1 || { printf '%s\n' "$input"; exit 0; }
13
13
 
14
14
  # PID scoping
15
15
  OUTCOMES_FILE="/tmp/.claude-task-outcomes-${PPID}"
16
- [ -f "$OUTCOMES_FILE" ] || { echo "$input"; exit 0; }
16
+ [ -f "$OUTCOMES_FILE" ] || { printf '%s\n' "$input"; exit 0; }
17
17
 
18
18
  # DB path
19
19
  DB_PATH="${HOME}/.config/oh-my-customcode/eval-core.sqlite"
20
- [ -f "$DB_PATH" ] || { echo "$input"; exit 0; }
20
+ [ -f "$DB_PATH" ] || { printf '%s\n' "$input"; exit 0; }
21
21
 
22
22
  # Log file for error diagnostics
23
23
  LOG_FILE="/tmp/.claude-feedback-collector-${PPID}.log"
@@ -30,8 +30,8 @@ declare -A FAILURE_COUNTS
30
30
  declare -A TOTAL_COUNTS
31
31
 
32
32
  while IFS= read -r line; do
33
- agent_type=$(echo "$line" | jq -r '.agent_type // empty' 2>/dev/null) || continue
34
- outcome=$(echo "$line" | jq -r '.outcome // empty' 2>/dev/null) || continue
33
+ agent_type=$(printf '%s\n' "$line" | jq -r '.agent_type // empty' 2>/dev/null) || continue
34
+ outcome=$(printf '%s\n' "$line" | jq -r '.outcome // empty' 2>/dev/null) || continue
35
35
  [ -z "$agent_type" ] && continue
36
36
 
37
37
  TOTAL_COUNTS[$agent_type]=$(( ${TOTAL_COUNTS[$agent_type]:-0} + 1 ))
@@ -88,5 +88,5 @@ fi
88
88
 
89
89
  # CRITICAL: Always pass through input and exit 0
90
90
  # This hook MUST NEVER block session termination
91
- echo "$input"
91
+ printf '%s\n' "$input"
92
92
  exit 0
@@ -3,11 +3,11 @@
3
3
  # Advisory warning when files are modified outside Claude Code
4
4
 
5
5
  input=$(cat)
6
- file_path=$(echo "$input" | jq -r '.file_path // ""' 2>/dev/null)
7
- change_type=$(echo "$input" | jq -r '.change_type // "modified"' 2>/dev/null)
6
+ file_path=$(printf '%s\n' "$input" | jq -r '.file_path // ""' 2>/dev/null)
7
+ change_type=$(printf '%s\n' "$input" | jq -r '.change_type // "modified"' 2>/dev/null)
8
8
 
9
9
  if [ -z "$file_path" ]; then
10
- echo "$input"
10
+ printf '%s\n' "$input"
11
11
  exit 0
12
12
  fi
13
13
 
@@ -23,4 +23,4 @@ case "$file_path" in
23
23
  ;;
24
24
  esac
25
25
 
26
- echo "$input"
26
+ printf '%s\n' "$input"
@@ -14,8 +14,8 @@ command -v jq >/dev/null 2>&1 || exit 0
14
14
 
15
15
  input=$(cat)
16
16
 
17
- agent_type=$(echo "$input" | jq -r '.tool_input.subagent_type // ""')
18
- prompt=$(echo "$input" | jq -r '.tool_input.prompt // ""')
17
+ agent_type=$(printf '%s\n' "$input" | jq -r '.tool_input.subagent_type // ""')
18
+ prompt=$(printf '%s\n' "$input" | jq -r '.tool_input.prompt // ""')
19
19
 
20
20
  # R010 violation tracking file (PPID-scoped for session persistence)
21
21
  VIOLATION_FILE="/tmp/.claude-r010-violations-${PPID}"
@@ -54,4 +54,4 @@ if [ "$agent_type" != "mgr-gitnerd" ]; then
54
54
  fi
55
55
 
56
56
  # Always pass through - this hook is advisory only
57
- echo "$input"
57
+ printf '%s\n' "$input"